﻿using System.Runtime.InteropServices;
using Microsoft.Ajax.Utilities;
using Newtonsoft.Json;
using Nop.Admin.Models.Common;
using Nop.Admin.Models.Orders;
using Nop.Core;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Common;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Directory;
using Nop.Core.Domain.Logistics;
using Nop.Core.Domain.Media;
using Nop.Core.Domain.Orders;
using Nop.Core.Domain.Payments;
using Nop.Core.Domain.Shipping;
using Nop.Core.Domain.Stores;
using Nop.Core.Domain.Tax;
using Nop.Services.Catalog;
using Nop.Services.Common;
using Nop.Services.Directory;
using Nop.Services.ExportImport;
using Nop.Services.Helpers;
using Nop.Services.Localization;
using Nop.Services.Logging;
using Nop.Services.Logistics;
using Nop.Services.Media;
using Nop.Services.Messages;
using Nop.Services.Orders;
using Nop.Services.Payments;
using Nop.Services.Security;
using Nop.Services.Shipping;
using Nop.Services.Stores;
using Nop.Services.Tax;
using Nop.Web.Framework;
using Nop.Web.Framework.Controllers;
using Nop.Web.Framework.Mvc;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Telerik.Web.Mvc;
using Nop.Core.Infrastructure;
using Nop.Services.Customers;

namespace Nop.Admin.Controllers
{
    [AdminAuthorize]
    public partial class OrderController : BaseNopController
    {
        #region Fields

        private readonly IOrderService _orderService;
        private readonly IOrderReportService _orderReportService;
        private readonly Nop.Services.Orders.IOrderProcessingService _orderProcessingService;
        private readonly Nop.Services.Logistics.IOrderProcessingService _logisticsOrderProcessService;
        private readonly IDateTimeHelper _dateTimeHelper;
        private readonly IPriceFormatter _priceFormatter;
        private readonly ILocalizationService _localizationService;
        private readonly IWorkContext _workContext;
        private readonly ICurrencyService _currencyService;
        private readonly IEncryptionService _encryptionService;
        private readonly IPaymentService _paymentService;
        private readonly IMeasureService _measureService;
        private readonly IPdfService _pdfService;
        private readonly IAddressService _addressService;
        private readonly ICountryService _countryService;
        private readonly IStateProvinceService _stateProvinceService;
        private readonly IProductService _productService;
        private readonly IExportManager _exportManager;
        private readonly IPermissionService _permissionService;
        private readonly IPictureService _pictureService;
        private readonly IWorkflowMessageService _workflowMessageService;
        private readonly ICategoryService _categoryService;
        private readonly IManufacturerService _manufacturerService;
        private readonly IProductAttributeService _productAttributeService;
        private readonly IProductAttributeParser _productAttributeParser;
        private readonly IProductAttributeFormatter _productAttributeFormatter;
        private readonly IShoppingCartService _shoppingCartService;
        private readonly IGiftCardService _giftCardService;
        private readonly IDownloadService _downloadService;
        private readonly IShipmentService _shipmentService;
        private readonly IStoreService _storeService;
        private readonly ILogisticsService _logisticsService;
        private readonly IInvoiceService _invoiceService;
        private readonly ITaxService _taxService;
        private readonly ICustomerActivityService _customerActivityService;
        private readonly ICheckoutAttributeParser _checkoutAttributeParser;
        private readonly HttpContextBase _httpContext;

        private readonly CatalogSettings _catalogSettings;
        private readonly CurrencySettings _currencySettings;
        private readonly TaxSettings _taxSettings;
        private readonly MeasureSettings _measureSettings;
        private readonly PdfSettings _pdfSettings;
        private readonly AddressSettings _addressSettings;
        private readonly LogisticsInformationSettings _logisticsInformationSettings;
        private readonly IWebHelper _webHelper;
        private readonly AdminAreaSettings _adminAreaSettings;
        private readonly IShippingService _shippingService;

        private string sender = "sender";
        private string receiver = "receiver";
        private string message = "message";

        #endregion

        #region Ctor

        public OrderController(IOrderService orderService,
            IOrderReportService orderReportService,
            Nop.Services.Orders.IOrderProcessingService orderProcessingService,
            Nop.Services.Logistics.IOrderProcessingService logisticsOrderProcessService,
            IDateTimeHelper dateTimeHelper, IPriceFormatter priceFormatter, ILocalizationService localizationService,
            IWorkContext workContext, ICurrencyService currencyService,
            IEncryptionService encryptionService, IPaymentService paymentService,
            IMeasureService measureService, IPdfService pdfService,
            IAddressService addressService, ICountryService countryService,
            IStateProvinceService stateProvinceService, IProductService productService,
            IExportManager exportManager, IPermissionService permissionService,
            IWorkflowMessageService workflowMessageService,
            ICategoryService categoryService, IManufacturerService manufacturerService,
            IProductAttributeService productAttributeService, IProductAttributeParser productAttributeParser,
            IProductAttributeFormatter productAttributeFormatter, IShoppingCartService shoppingCartService,
            IGiftCardService giftCardService, IDownloadService downloadService,
            IShipmentService shipmentService, IPictureService pictureService,
            IStoreService storeService,
            ILogisticsService logisticsService,
            IInvoiceService invoiceService,
            ITaxService taxService,
            ICustomerActivityService customerActivityService,
            HttpContextBase httpContext,
            CatalogSettings catalogSettings, CurrencySettings currencySettings, TaxSettings taxSettings,
            MeasureSettings measureSettings, PdfSettings pdfSettings, AddressSettings addressSettings,
            LogisticsInformationSettings logisticsInformationSettings,
            IWebHelper webHelper, AdminAreaSettings adminAreaSettings,
            IShippingService shippingService,
            ICheckoutAttributeParser checkoutAttributeParser)
        {
            this._orderService = orderService;
            this._orderReportService = orderReportService;
            this._orderProcessingService = orderProcessingService;
            this._logisticsOrderProcessService = logisticsOrderProcessService;
            this._dateTimeHelper = dateTimeHelper;
            this._priceFormatter = priceFormatter;
            this._localizationService = localizationService;
            this._workContext = workContext;
            this._currencyService = currencyService;
            this._encryptionService = encryptionService;
            this._paymentService = paymentService;
            this._measureService = measureService;
            this._pdfService = pdfService;
            this._addressService = addressService;
            this._countryService = countryService;
            this._stateProvinceService = stateProvinceService;
            this._productService = productService;
            this._exportManager = exportManager;
            this._permissionService = permissionService;
            this._workflowMessageService = workflowMessageService;
            this._categoryService = categoryService;
            this._manufacturerService = manufacturerService;
            this._productAttributeService = productAttributeService;
            this._taxService = taxService;
            this._customerActivityService = customerActivityService;
            this._productAttributeParser = productAttributeParser;
            this._productAttributeFormatter = productAttributeFormatter;
            this._shoppingCartService = shoppingCartService;
            this._giftCardService = giftCardService;
            this._downloadService = downloadService;
            this._shipmentService = shipmentService;
            this._pictureService = pictureService;
            this._storeService = storeService;
            this._logisticsService = logisticsService;
            this._invoiceService = invoiceService;
            this._checkoutAttributeParser = checkoutAttributeParser;
            this._httpContext = httpContext;

            this._catalogSettings = catalogSettings;
            this._currencySettings = currencySettings;
            this._taxSettings = taxSettings;
            this._measureSettings = measureSettings;
            this._pdfSettings = pdfSettings;
            this._addressSettings = addressSettings;
            this._logisticsInformationSettings = logisticsInformationSettings;
            this._webHelper = webHelper;
            this._adminAreaSettings = adminAreaSettings;
            this._shippingService = shippingService;
        }

        #endregion

        #region Utilities

        [NonAction]
        protected bool UpdateInvoiceAfterOrderChange(Invoice invoice, int orderId, bool cancelPackage = false)
        {
            if (invoice != null && invoice.Orders != null && invoice.Orders.Count > 0)
            {
                var charge = _invoiceService.GetTotalCharge(invoice.Orders);

                invoice.SubTotal = charge.SubTotal;
                invoice.FuelSurcharge = charge.FuelSurchargeTotal;
                invoice.MonthlyCharge = charge.MonthlyChargeTotal;
                invoice.StorageCharge = charge.StorageChargeTotal;
                invoice.TransactionProcessingCharge = charge.TransactionProcessingChargeTotal;
                invoice.ServiceCharge = charge.ServiceChargeTotal;
                invoice.Tax = charge.TaxTotal;
                invoice.Total = charge.Total;
                invoice.InvoiceNotes.Add(new InvoiceNote()
                {
                    Note = string.Format(_localizationService.GetResource("Admin.Invoice.OrdersChanged"), orderId, _workContext.CurrentCustomer.Email),
                    DisplayToCustomer = false,
                    CreatedOnUtc = DateTime.UtcNow
                });
                _invoiceService.UpdateInvoice(invoice);
                return true;
            }

            return false;
        }

        [NonAction]
        protected void PrepareOrderDetailsModel(OrderModel model, Order order)
        {
            if (order == null)
                throw new ArgumentNullException("order");

            if (model == null)
                throw new ArgumentNullException("model");

            model.Id = order.Id;
            model.BoundOrderId = order.BoundOrderId;
            model.OrderStatus = order.OrderStatus.GetLocalizedEnum(_localizationService, _workContext);
            model.OrderGuid = order.OrderGuid;
            model.CustomerId = order.CustomerId;
            model.CustomerIp = order.CustomerIp;
            model.VatNumber = order.VatNumber;
            model.CreatedOn = _dateTimeHelper.ConvertToUserTime(order.CreatedOnUtc, DateTimeKind.Utc);
            model.DisplayPdfInvoice = _pdfSettings.Enabled;
            model.AllowCustomersToSelectTaxDisplayType = _taxSettings.AllowCustomersToSelectTaxDisplayType;
            model.TaxDisplayType = _taxSettings.TaxDisplayType;
            model.AffiliateId = order.AffiliateId;

            model.OrderKind = order.OrderKind;

            #region Order totals

            var primaryStoreCurrency = _currencyService.GetCurrencyById(_currencySettings.PrimaryStoreCurrencyId);

            //subtotal
            model.OrderSubtotalInclTax = _priceFormatter.FormatPrice(order.OrderSubtotalInclTax, true, primaryStoreCurrency, _workContext.WorkingLanguage, true);
            model.OrderSubtotalExclTax = _priceFormatter.FormatPrice(order.OrderSubtotalExclTax, true, primaryStoreCurrency, _workContext.WorkingLanguage, false);
            model.OrderSubtotalInclTaxValue = order.OrderSubtotalInclTax;
            model.OrderSubtotalExclTaxValue = order.OrderSubtotalExclTax;
            //discount (applied to order subtotal)
            string orderSubtotalDiscountInclTaxStr = _priceFormatter.FormatPrice(order.OrderSubTotalDiscountInclTax, true, primaryStoreCurrency, _workContext.WorkingLanguage, true);
            string orderSubtotalDiscountExclTaxStr = _priceFormatter.FormatPrice(order.OrderSubTotalDiscountExclTax, true, primaryStoreCurrency, _workContext.WorkingLanguage, false);
            if (order.OrderSubTotalDiscountInclTax > decimal.Zero)
                model.OrderSubTotalDiscountInclTax = orderSubtotalDiscountInclTaxStr;
            if (order.OrderSubTotalDiscountExclTax > decimal.Zero)
                model.OrderSubTotalDiscountExclTax = orderSubtotalDiscountExclTaxStr;
            model.OrderSubTotalDiscountInclTaxValue = order.OrderSubTotalDiscountInclTax;
            model.OrderSubTotalDiscountExclTaxValue = order.OrderSubTotalDiscountExclTax;

            //shipping
            model.OrderShippingInclTax = _priceFormatter.FormatShippingPrice(order.OrderShippingInclTax, true, primaryStoreCurrency, _workContext.WorkingLanguage, true);
            model.OrderShippingExclTax = _priceFormatter.FormatShippingPrice(order.OrderShippingExclTax, true, primaryStoreCurrency, _workContext.WorkingLanguage, false);
            model.OrderShippingInclTaxValue = order.OrderShippingInclTax;
            model.OrderShippingExclTaxValue = order.OrderShippingExclTax;

            //payment method additional fee
            if (order.PaymentMethodAdditionalFeeInclTax > decimal.Zero)
            {
                model.PaymentMethodAdditionalFeeInclTax = _priceFormatter.FormatPaymentMethodAdditionalFee(order.PaymentMethodAdditionalFeeInclTax, true, primaryStoreCurrency, _workContext.WorkingLanguage, true);
                model.PaymentMethodAdditionalFeeExclTax = _priceFormatter.FormatPaymentMethodAdditionalFee(order.PaymentMethodAdditionalFeeExclTax, true, primaryStoreCurrency, _workContext.WorkingLanguage, false);
            }
            model.PaymentMethodAdditionalFeeInclTaxValue = order.PaymentMethodAdditionalFeeInclTax;
            model.PaymentMethodAdditionalFeeExclTaxValue = order.PaymentMethodAdditionalFeeExclTax;


            //tax
            model.Tax = _priceFormatter.FormatPrice(order.OrderTax, true, false);
            var taxRates = _taxService.GetSurchargesByOrder(order).ToTaxes().ToDistinctTypes();
            bool displayTaxRates = _taxSettings.DisplayTaxRates && taxRates.Count > 0;
            bool displayTax = !displayTaxRates;
            foreach (var tr in taxRates)
            {
                model.TaxRates.Add(new OrderModel.TaxRate()
                {
                    Rate = _priceFormatter.FormatTaxRate(tr),
                    Value = order.OrderSubtotalInclTax > 0 && order.OrderSubTotalDiscountExclTax > 0 ? _priceFormatter.FormatPrice(tr.Value, true, false) : _priceFormatter.FormatPrice(0, true, false),
                });
            }
            model.DisplayTaxRates = displayTaxRates;
            model.DisplayTax = displayTax;
            model.TaxValue = order.OrderTax;
            model.TaxRatesValue = order.TaxRates;

            //discount
            if (order.OrderDiscount > 0)
                model.OrderTotalDiscount = _priceFormatter.FormatPrice(-order.OrderDiscount, true, false);
            model.OrderTotalDiscountValue = order.OrderDiscount;

            //gift cards
            foreach (var gcuh in order.GiftCardUsageHistory)
            {
                model.GiftCards.Add(new OrderModel.GiftCard()
                {
                    CouponCode = gcuh.GiftCard.GiftCardCouponCode,
                    Amount = _priceFormatter.FormatPrice(-gcuh.UsedValue, true, false),
                });
            }

            //reward points
            if (order.RedeemedRewardPointsEntry != null)
            {
                model.RedeemedRewardPoints = -order.RedeemedRewardPointsEntry.Points;
                model.RedeemedRewardPointsAmount = _priceFormatter.FormatPrice(-order.RedeemedRewardPointsEntry.UsedAmount, true, false);
            }

            //total
            model.OrderTotal = _priceFormatter.FormatPrice(order.OrderTotal, true, false);
            model.OrderTotalValue = order.OrderTotal;

            //refunded amount
            if (order.RefundedAmount > decimal.Zero)
                model.RefundedAmount = _priceFormatter.FormatPrice(order.RefundedAmount, true, false);


            #endregion

            #region Payment info

            if (order.AllowStoringCreditCardNumber)
            {
                //card type
                model.CardType = _encryptionService.DecryptText(order.CardType);
                //cardholder name
                model.CardName = _encryptionService.DecryptText(order.CardName);
                //card number
                model.CardNumber = _encryptionService.DecryptText(order.CardNumber);
                //cvv
                model.CardCvv2 = _encryptionService.DecryptText(order.CardCvv2);
                //expiry date
                string cardExpirationMonthDecrypted = _encryptionService.DecryptText(order.CardExpirationMonth);
                if (!String.IsNullOrEmpty(cardExpirationMonthDecrypted) && cardExpirationMonthDecrypted != "0")
                    model.CardExpirationMonth = cardExpirationMonthDecrypted;
                string cardExpirationYearDecrypted = _encryptionService.DecryptText(order.CardExpirationYear);
                if (!String.IsNullOrEmpty(cardExpirationYearDecrypted) && cardExpirationYearDecrypted != "0")
                    model.CardExpirationYear = cardExpirationYearDecrypted;

                model.AllowStoringCreditCardNumber = true;
            }
            else
            {
                string maskedCreditCardNumberDecrypted = _encryptionService.DecryptText(order.MaskedCreditCardNumber);
                if (!String.IsNullOrEmpty(maskedCreditCardNumberDecrypted))
                    model.CardNumber = maskedCreditCardNumberDecrypted;
            }


            //purchase order number (we have to find a better to inject this information because it's related to a certain plugin)
            var pm = _paymentService.LoadPaymentMethodBySystemName(order.PaymentMethodSystemName);
            if (pm != null && pm.PluginDescriptor.SystemName.Equals("Payments.PurchaseOrder", StringComparison.InvariantCultureIgnoreCase))
            {
                model.DisplayPurchaseOrderNumber = true;
                model.PurchaseOrderNumber = order.PurchaseOrderNumber;
            }

            //payment transaction info
            model.AuthorizationTransactionId = order.AuthorizationTransactionId;
            model.CaptureTransactionId = order.CaptureTransactionId;
            model.SubscriptionTransactionId = order.SubscriptionTransactionId;

            //payment method info
            model.PaymentMethod = pm != null ? pm.PluginDescriptor.FriendlyName : order.PaymentMethodSystemName;
            model.PaymentStatus = order.PaymentStatus.GetLocalizedEnum(_localizationService, _workContext);

            //payment method buttons
            model.CanCancelOrder = _orderProcessingService.CanCancelOrder(order);
            model.CanCapture = _orderProcessingService.CanCapture(order);
            model.CanMarkOrderAsPaid = _orderProcessingService.CanMarkOrderAsPaid(order);
            model.CanRefund = _orderProcessingService.CanRefund(order);
            model.CanRefundOffline = _orderProcessingService.CanRefundOffline(order);
            model.CanPartiallyRefund = false;
            model.CanPartiallyRefundOffline = false;
            if (model.OrderKind == OrderKind.Commerce)
            {
                //only commerce orders with more than 1 item can partially refund
                model.CanPartiallyRefund = _orderProcessingService.CanPartiallyRefund(order, decimal.Zero);
                model.CanPartiallyRefundOffline = _orderProcessingService.CanPartiallyRefundOffline(order, decimal.Zero);
            }
            model.CanVoid = _orderProcessingService.CanVoid(order);
            model.CanVoidOffline = _orderProcessingService.CanVoidOffline(order);

            model.PrimaryStoreCurrencyCode = _currencyService.GetCurrencyById(_currencySettings.PrimaryStoreCurrencyId).CurrencyCode;
            model.MaxAmountToRefund = order.OrderTotal - order.RefundedAmount;

            //recurring payment record
            var recurringPayment = _orderService.SearchRecurringPayments(0, order.Id, null, 0, int.MaxValue, true).FirstOrDefault();
            if (recurringPayment != null)
            {
                model.RecurringPaymentId = recurringPayment.Id;
            }
            #endregion

            #region Billing & shipping info

            model.BillingAddress = order.BillingAddress.ToModel();
            model.BillingAddress.FirstNameEnabled = true;
            model.BillingAddress.FirstNameRequired = true;
            model.BillingAddress.LastNameEnabled = true;
            model.BillingAddress.LastNameRequired = true;
            model.BillingAddress.EmailEnabled = true;
            model.BillingAddress.EmailRequired = true;
            model.BillingAddress.CellPhoneEnabled = _addressSettings.CellPhoneEnabled;
            model.BillingAddress.CellPhoneRequired = _addressSettings.CellPhoneRequired;
            model.BillingAddress.CompanyEnabled = _addressSettings.CompanyEnabled;
            model.BillingAddress.CompanyRequired = _addressSettings.CompanyRequired;
            model.BillingAddress.CountryEnabled = _addressSettings.CountryEnabled;
            model.BillingAddress.StateProvinceEnabled = _addressSettings.StateProvinceEnabled;
            model.BillingAddress.CityEnabled = _addressSettings.CityEnabled;
            model.BillingAddress.CityRequired = _addressSettings.CityRequired;
            model.BillingAddress.StreetAddressEnabled = _addressSettings.StreetAddressEnabled;
            model.BillingAddress.StreetAddressRequired = _addressSettings.StreetAddressRequired;
            model.BillingAddress.StreetAddress2Enabled = _addressSettings.StreetAddress2Enabled;
            model.BillingAddress.StreetAddress2Required = _addressSettings.StreetAddress2Required;
            model.BillingAddress.ZipPostalCodeEnabled = _addressSettings.ZipPostalCodeEnabled;
            model.BillingAddress.ZipPostalCodeRequired = _addressSettings.ZipPostalCodeRequired;
            model.BillingAddress.PhoneEnabled = _addressSettings.PhoneEnabled;
            model.BillingAddress.PhoneRequired = _addressSettings.PhoneRequired;
            model.BillingAddress.FaxEnabled = _addressSettings.FaxEnabled;
            model.BillingAddress.FaxRequired = _addressSettings.FaxRequired;

            model.ShippingStatus = order.ShippingStatus.GetLocalizedEnum(_localizationService, _workContext); ;
            if (order.ShippingStatus != ShippingStatus.ShippingNotRequired)
            {
                model.IsShippable = true;

                model.ShippingAddress = order.ShippingAddress.ToModel();
                model.ShippingAddress.FirstNameEnabled = true;
                model.ShippingAddress.FirstNameRequired = true;
                model.ShippingAddress.LastNameEnabled = true;
                model.ShippingAddress.LastNameRequired = true;
                model.ShippingAddress.EmailEnabled = true;
                model.ShippingAddress.EmailRequired = true;
                model.ShippingAddress.CellPhoneEnabled = _addressSettings.CellPhoneEnabled;
                model.ShippingAddress.CellPhoneRequired = _addressSettings.CellPhoneRequired;
                model.ShippingAddress.CompanyEnabled = _addressSettings.CompanyEnabled;
                model.ShippingAddress.CompanyRequired = _addressSettings.CompanyRequired;
                model.ShippingAddress.CountryEnabled = _addressSettings.CountryEnabled;
                model.ShippingAddress.StateProvinceEnabled = _addressSettings.StateProvinceEnabled;
                model.ShippingAddress.CityEnabled = _addressSettings.CityEnabled;
                model.ShippingAddress.CityRequired = _addressSettings.CityRequired;
                model.ShippingAddress.StreetAddressEnabled = _addressSettings.StreetAddressEnabled;
                model.ShippingAddress.StreetAddressRequired = _addressSettings.StreetAddressRequired;
                model.ShippingAddress.StreetAddress2Enabled = _addressSettings.StreetAddress2Enabled;
                model.ShippingAddress.StreetAddress2Required = _addressSettings.StreetAddress2Required;
                model.ShippingAddress.ZipPostalCodeEnabled = _addressSettings.ZipPostalCodeEnabled;
                model.ShippingAddress.ZipPostalCodeRequired = _addressSettings.ZipPostalCodeRequired;
                model.ShippingAddress.PhoneEnabled = _addressSettings.PhoneEnabled;
                model.ShippingAddress.PhoneRequired = _addressSettings.PhoneRequired;
                model.ShippingAddress.FaxEnabled = _addressSettings.FaxEnabled;
                model.ShippingAddress.FaxRequired = _addressSettings.FaxRequired;

                model.ShippingMethod = order.ShippingMethod;
                model.ShippingRateComputationMethodSystemName = order.ShippingRateComputationMethodSystemName;

                model.ShippingAddressGoogleMapsUrl = string.Format("http://maps.google.com/maps?f=q&hl=en&ie=UTF8&oe=UTF8&geocode=&q={0}", Server.UrlEncode(order.ShippingAddress.Address1 + " " + order.ShippingAddress.ZipPostalCode + " " + order.ShippingAddress.City + " " + (order.ShippingAddress.Country != null ? order.ShippingAddress.Country.Name : "")));
                model.CanAddNewShipments = order.HasItemsToAddToShipment();
                if (order.PaymentStatus != PaymentStatus.Paid)
                    model.CanAddNewShipments = false;
            }

            #endregion

            #region Products
            model.CheckoutAttributeInfo = order.CheckoutAttributeDescription;
            model.CheckoutAttributeViewModel = _checkoutAttributeParser.ParseCheckoutAttribute(order.CheckoutAttributesXml);
            bool hasDownloadableItems = false;
            foreach (var opv in order.OrderProductVariants)
            {
                if (opv.ProductVariant != null && opv.ProductVariant.IsDownload)
                    hasDownloadableItems = true;

                var opvModel = new OrderModel.OrderProductVariantModel()
                {
                    Id = opv.Id,
                    ProductVariantId = opv.ProductVariantId,
                    Sku = opv.ProductVariant.Sku,
                    Quantity = opv.Quantity,
                    IsDownload = opv.ProductVariant.IsDownload,
                    DownloadCount = opv.DownloadCount,
                    DownloadActivationType = opv.ProductVariant.DownloadActivationType,
                    IsDownloadActivated = opv.IsDownloadActivated,
                    LicenseDownloadId = opv.LicenseDownloadId,
                    IsLogisticsPackage =
                        opv.ProductVariantId == _logisticsInformationSettings.PackageForMemberProductVarianId ||
                        opv.ProductVariantId == _logisticsInformationSettings.PackageNonMemberProductVarianId
                };

                //product name
                if (!String.IsNullOrEmpty(opv.ProductVariant.Name))
                    opvModel.FullProductName = string.Format("{0} ({1})", opv.ProductVariant.Product.Name, opv.ProductVariant.Name);
                else
                    opvModel.FullProductName = opv.ProductVariant.Product.Name;

                //unit price
                opvModel.UnitPriceInclTaxValue = opv.UnitPriceInclTax;
                opvModel.UnitPriceExclTaxValue = opv.UnitPriceExclTax;
                opvModel.UnitPriceInclTax = _priceFormatter.FormatPrice(opv.UnitPriceInclTax, true, primaryStoreCurrency, _workContext.WorkingLanguage, true, true);
                opvModel.UnitPriceExclTax = _priceFormatter.FormatPrice(opv.UnitPriceExclTax, true, primaryStoreCurrency, _workContext.WorkingLanguage, false, true);
                //discounts
                opvModel.DiscountInclTaxValue = opv.DiscountAmountInclTax;
                opvModel.DiscountExclTaxValue = opv.DiscountAmountExclTax;
                opvModel.DiscountInclTax = _priceFormatter.FormatPrice(opv.DiscountAmountInclTax, true, primaryStoreCurrency, _workContext.WorkingLanguage, true, true);
                opvModel.DiscountExclTax = _priceFormatter.FormatPrice(opv.DiscountAmountExclTax, true, primaryStoreCurrency, _workContext.WorkingLanguage, false, true);
                //subtotal
                opvModel.SubTotalInclTaxValue = opv.PriceInclTax;
                opvModel.SubTotalExclTaxValue = opv.PriceExclTax;
                opvModel.SubTotalInclTax = _priceFormatter.FormatPrice(opv.PriceInclTax, true, primaryStoreCurrency, _workContext.WorkingLanguage, true, true);
                opvModel.SubTotalExclTax = _priceFormatter.FormatPrice(opv.PriceExclTax, true, primaryStoreCurrency, _workContext.WorkingLanguage, false, true);

                opvModel.AttributeInfo = opv.AttributeDescription;
                if (opv.ProductVariant.IsRecurring)
                    opvModel.RecurringInfo = string.Format(_localizationService.GetResource("Admin.Orders.Products.RecurringPeriod"), opv.ProductVariant.RecurringCycleLength, opv.ProductVariant.RecurringCyclePeriod.GetLocalizedEnum(_localizationService, _workContext));

                //return requests
                opvModel.ReturnRequestIds = _orderService.SearchReturnRequests(0, opv.Id, null, 0, int.MaxValue)
                    .Select(rr => rr.Id).ToList();
                //gift cards
                opvModel.PurchasedGiftCardIds = _giftCardService.GetGiftCardsByPurchasedWithOrderProductVariantId(opv.Id)
                    .Select(gc => gc.Id).ToList();

                model.Items.Add(opvModel);
            }
            model.HasDownloadableProducts = hasDownloadableItems;
            #endregion

            #region Shipment date

            model.ShipmentDate = order.ShipmentDate;
            model.ShipmentTimeRange = order.ShippingTimeWindowsId.HasValue ? _logisticsService.GetShippingTimeWindowById(order.ShippingTimeWindowsId.Value) : null;
            model.ShowPickUpTime = _logisticsService.GetShowPickUpTime(order.ShipmentDate, order.ShippingTimeWindowsId,
                order);

            #endregion
        }

        protected Charge CalculateChargeTotal(ShipmentPackageModel model, LogisticsPackage package)
        {
            var shipment = package.Shipment;
            var customer = shipment.Order.Customer;
            var charge = new Charge();
            var attributes = (AddtionalAttributes)shipment.Attributes;
            var oi = new OrderInformation()
            {
                UserAddressId = customer.BillingAddress.Id,
                IsCommerce = attributes.HasFlag(AddtionalAttributes.ECommerce)
            };
            var li = new LocationInformation()
            {
                PackageNumber = 1,
                IsUserAddressPickup = true,
                AddressId = customer.ShippingAddress.Id,
            };

            oi.Locations.Add(li);

            var pi = new PackageInformation()
            {
                AddtionalAttributes = attributes,
                InsuranceValue = shipment.InsuranceValue
            };
            if (model != null)
            {
                pi.Length = model.Length;
                pi.Width = model.Width;
                pi.Height = model.Height;
                pi.Weight = model.Weight;
                pi.IsLettersize = false;
                pi.IsCompactSize = false;
                pi.IsFullSize = false;
            }
            else
            {
                int spotshubPackageSize = package.Shipment.SpotshubPackageSize;
                bool hasPackageSize = true;
                switch (spotshubPackageSize)
                {
                    case 0: hasPackageSize = false;
                        break;
                    case 1: pi.IsLettersize = true;
                        pi.IsCompactSize = false;
                        pi.IsFullSize = false;
                        break;
                    case 2: pi.IsLettersize = false;
                        pi.IsCompactSize = true;
                        pi.IsFullSize = false;
                        break;
                    case 3: pi.IsLettersize = false;
                        pi.IsCompactSize = false;
                        pi.IsFullSize = true;
                        break;
                }
                if (!hasPackageSize)
                {
                    pi.Length = shipment.Length;
                    pi.Width = shipment.Width;
                    pi.Height = shipment.Height;
                    pi.Weight = shipment.TotalWeight;
                }

            }
            li.Packages.Add(pi);
            charge = _logisticsOrderProcessService.CaculateTotal(oi, customer);
            return charge;
        }

        [NonAction]
        protected OrderModel.AddOrderProductModel.ProductDetailsModel PrepareAddProductToOrderModel(int orderId, int productVariantId)
        {

            var productVariant = _productService.GetProductVariantById(productVariantId);
            var model = new OrderModel.AddOrderProductModel.ProductDetailsModel()
            {
                ProductVariantId = productVariantId,
                OrderId = orderId,
                Name = productVariant.FullProductName,
                UnitPriceExclTax = decimal.Zero,
                UnitPriceInclTax = decimal.Zero,
                Quantity = 1,
                SubTotalExclTax = decimal.Zero,
                SubTotalInclTax = decimal.Zero
            };

            //attributes
            var productVariantAttributes = _productAttributeService.GetProductVariantAttributesByProductVariantId(productVariant.Id);
            foreach (var attribute in productVariantAttributes)
            {
                var pvaModel = new OrderModel.AddOrderProductModel.ProductVariantAttributeModel()
                {
                    Id = attribute.Id,
                    ProductAttributeId = attribute.ProductAttributeId,
                    Name = attribute.ProductAttribute.Name,
                    TextPrompt = attribute.TextPrompt,
                    IsRequired = attribute.IsRequired,
                    AttributeControlType = attribute.AttributeControlType
                };

                if (attribute.ShouldHaveValues())
                {
                    //values
                    var pvaValues = _productAttributeService.GetProductVariantAttributeValues(attribute.Id);
                    foreach (var pvaValue in pvaValues)
                    {
                        var pvaValueModel = new OrderModel.AddOrderProductModel.ProductVariantAttributeValueModel()
                        {
                            Id = pvaValue.Id,
                            Name = pvaValue.Name,
                            IsPreSelected = pvaValue.IsPreSelected
                        };
                        pvaModel.Values.Add(pvaValueModel);
                    }
                }

                model.ProductVariantAttributes.Add(pvaModel);
            }
            //gift card
            model.GiftCard.IsGiftCard = productVariant.IsGiftCard;
            if (model.GiftCard.IsGiftCard)
            {
                model.GiftCard.GiftCardType = productVariant.GiftCardType;
            }
            return model;
        }

        [NonAction]
        protected ShipmentModel PrepareShipmentModel(Shipment shipment, bool prepareProducts)
        {
            //measures
            var baseWeight = _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId);
            var baseWeightIn = baseWeight != null ? baseWeight.Name : "";
            var baseDimension = _measureService.GetMeasureDimensionById(_measureSettings.BaseDimensionId);
            var baseDimensionIn = baseDimension != null ? baseDimension.Name : "";

            var model = new ShipmentModel()
            {
                Id = shipment.Id,
                OrderId = shipment.OrderId,
                TrackingNumber = shipment.TrackingNumber,
                TotalWeight = shipment.TotalWeight.HasValue ? string.Format("{0:F3} [{1}]", shipment.TotalWeight, baseWeightIn) : "",
                Length = shipment.Length.HasValue ? string.Format("{0:F2} [{1}]", shipment.Length, baseDimensionIn) : "",
                Width = shipment.Width.HasValue ? string.Format("{0:F2} [{1}]", shipment.Width, baseDimensionIn) : "",
                Height = shipment.Height.HasValue ? string.Format("{0:F2} [{1}]", shipment.Height, baseDimensionIn) : "",
                ShippedDate = shipment.ShippedDateUtc.HasValue ? _dateTimeHelper.ConvertToUserTime(shipment.ShippedDateUtc.Value, DateTimeKind.Utc).ToString() : _localizationService.GetResource("Admin.Orders.Shipments.ShippedDate.NotYet"),
                CanShip = !shipment.ShippedDateUtc.HasValue,
                DeliveryDate = shipment.DeliveryDateUtc.HasValue ? _dateTimeHelper.ConvertToUserTime(shipment.DeliveryDateUtc.Value, DateTimeKind.Utc).ToString() : _localizationService.GetResource("Admin.Orders.Shipments.DeliveryDate.NotYet"),
                CanDeliver = shipment.ShippedDateUtc.HasValue && !shipment.DeliveryDateUtc.HasValue,
                DisplayPdfPackagingSlip = _pdfSettings.Enabled,
                PackageId = null,
                PackageStatus = "N/A"
            };

            LogisticsPackage pkg = null;

            if (shipment.PackageId.HasValue)
            {
                pkg = _logisticsService.GetPackageById(shipment.PackageId.Value);
            }
            else
            {
                pkg = _logisticsService.GetPackageByShipmentId(shipment.Id);
            }
            if (pkg != null)
            {
                model.PackageId = pkg.Id;
                model.PackageStatus = pkg.Status.GetLocalizedEnum(_localizationService, _workContext);
            }

            if (prepareProducts)
            {
                foreach (var sopv in shipment.ShipmentOrderProductVariants)
                {
                    var opv = _orderService.GetOrderProductVariantById(sopv.OrderProductVariantId);
                    if (opv == null)
                        continue;

                    //quantities
                    var qtyInThisShipment = sopv.Quantity;
                    var maxQtyToAdd = opv.GetTotalNumberOfItemsCanBeAddedToShipment();
                    var qtyOrdered = opv.Quantity;
                    var qtyInAllShipments = opv.GetTotalNumberOfItemsInAllShipment();

                    var sopvModel = new ShipmentModel.ShipmentOrderProductVariantModel()
                    {
                        Id = sopv.Id,
                        OrderProductVariantId = opv.Id,
                        ProductVariantId = opv.ProductVariantId,
                        Sku = opv.ProductVariant.Sku,
                        AttributeInfo = opv.AttributeDescription,
                        ItemWeight = opv.ItemWeight.HasValue ? string.Format("{0:F2} [{1}]", opv.ItemWeight, baseWeightIn) : "",
                        ItemDimensions = string.Format("{0:F2} x {1:F2} x {2:F2} [{3}]", opv.ProductVariant.Length, opv.ProductVariant.Width, opv.ProductVariant.Height, baseDimensionIn),
                        QuantityOrdered = qtyOrdered,
                        QuantityInThisShipment = qtyInThisShipment,
                        QuantityInAllShipments = qtyInAllShipments,
                        QuantityToAdd = maxQtyToAdd,
                    };

                    //product name
                    if (!String.IsNullOrEmpty(opv.ProductVariant.Name))
                        sopvModel.FullProductName = string.Format("{0} ({1})", opv.ProductVariant.Product.Name,
                                                                  opv.ProductVariant.Name);
                    else
                        sopvModel.FullProductName = opv.ProductVariant.Product.Name;
                    model.Products.Add(sopvModel);
                }
            }

            return model;
        }

        [NonAction]
        protected void PrepareAddShipmentModel(ShipmentModel model, Order order)
        {
            //measures
            var baseWeight = _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId);
            var baseWeightIn = baseWeight != null ? baseWeight.Name : "";
            var baseDimension = _measureService.GetMeasureDimensionById(_measureSettings.BaseDimensionId);
            var baseDimensionIn = baseDimension != null ? baseDimension.Name : "";
            var dims = _measureService.GetAllMeasureDimensions().ToList();
            var weights = _measureService.GetAllMeasureWeights().ToList();
            /*
            if (model.CurrentPackage.WeightMeasureId == 0)
                model.CurrentPackage.WeightMeasureId = _measureSettings.BaseWeightId;
            if (model.CurrentPackage.DimensionMeasureId == 0)
                model.CurrentPackage.DimensionMeasureId = _measureSettings.BaseDimensionId;
            */
            model.Products = new List<ShipmentModel.ShipmentOrderProductVariantModel>();
            model.CanAddAll = true;
            model.DimensionMeasureId = _measureSettings.BaseDimensionId;
            model.WeightMeasureId = _measureSettings.BaseWeightId;
            model.AvaliableWeightMeasure = weights.Select(x => new SelectListItem() { Selected = x.Id == model.WeightMeasureId, Text = x.Name, Value = x.Id.ToString() }).ToList();
            model.AvaliableDimensionMeasure = dims.Select(x => new SelectListItem() { Selected = x.Id == model.DimensionMeasureId, Text = x.Name, Value = x.Id.ToString() }).ToList();
            //allow third party
            string spotsHUBEXPRESS = _localizationService.GetResource("Admin.ShippingRate.ComputationMethods.SpotsHUBEXPRESS");
            //model.IsThirdPartyShippingOption = false;
            model.ShippingRateComputationMethodSystemName = order.ShippingRateComputationMethodSystemName;
            if (!String.IsNullOrEmpty(spotsHUBEXPRESS) && order.ShippingRateComputationMethodSystemName != spotsHUBEXPRESS)
            {
                model.IsThirdPartyShippingOption = true;
            }
            model.IsThirdPartyShippingOption = false;
            foreach (var opv in order.OrderProductVariants)
            {
                //we can ship only shippable products
                if (!opv.ProductVariant.IsShipEnabled)
                    continue;

                //quantities
                var qtyInThisShipment = model.Packages.SelectMany(x => x.Products).Where(x => x.ProductVariantId == opv.ProductVariantId).Sum(x => x.QuantityToAdd);
                var maxQtyToAdd = opv.GetTotalNumberOfItemsCanBeAddedToShipment();
                var qtyOrdered = opv.Quantity;
                var qtyInAllShipments = opv.GetTotalNumberOfItemsInAllShipment();
                var qtyToAdd = maxQtyToAdd - qtyInAllShipments - qtyInThisShipment;

                //product pictures
                string pictureUrl = "";
                if (_adminAreaSettings.DisplayProductPictures)
                {
                    var productPictures = _productService.GetProductPicturesByProductId(opv.ProductVariant.ProductId);
                    if (productPictures.Count != 0 && productPictures != null)
                        pictureUrl = _pictureService.GetPictureUrl(productPictures.FirstOrDefault().Picture, 75, false);

                    if (String.IsNullOrEmpty(pictureUrl))
                    {
                        var defaultProductPicture = new Picture();
                        pictureUrl = _pictureService.GetPictureUrl(defaultProductPicture, 75, true);
                    }
                }

                //ensure that this product variant can be added to a shipment
                if (qtyToAdd <= 0)
                    continue;

                //at least one package created
                if (qtyInAllShipments > 0)
                    model.CanAddAll = false;

                var sopvModel = new ShipmentModel.ShipmentOrderProductVariantModel()
                {
                    OrderProductVariantId = opv.Id,
                    ProductVariantId = opv.ProductVariantId,
                    Sku = opv.ProductVariant.Sku,
                    AttributeInfo = opv.AttributeDescription,
                    ItemWeight = opv.ItemWeight.HasValue ? string.Format("{0:F2} [{1}]", opv.ItemWeight, baseWeightIn) : "",
                    ItemDimensions = string.Format("{0:F2} x {1:F2} x {2:F2} [{3}]", opv.ProductVariant.Length, opv.ProductVariant.Width, opv.ProductVariant.Height, baseDimensionIn),
                    QuantityOrdered = qtyOrdered,
                    QuantityInThisShipment = qtyInThisShipment,
                    QuantityInAllShipments = qtyInAllShipments,
                    QuantityToAdd = qtyToAdd,
                    ProductPictureUrl = pictureUrl
                };

                //product name
                if (!String.IsNullOrEmpty(opv.ProductVariant.Name))
                    sopvModel.FullProductName = string.Format("{0} ({1})", opv.ProductVariant.Product.Name, opv.ProductVariant.Name);
                else
                    sopvModel.FullProductName = opv.ProductVariant.Product.Name;

                model.Products.Add(sopvModel);
                if (qtyToAdd > 0)
                    model.UnpackagedProducts.Add(sopvModel);
            }

            if (model.Products.Count != 0)
            {
                /*
                model.CurrentPackage.AvaliableDimensionMeasure = dims.Select(x => new SelectListItem() { Selected = x.Id == model.CurrentPackage.DimensionMeasureId, Text = x.Name, Value = x.Id.ToString() }).ToList();
                model.CurrentPackage.AvaliableWeightMeasure = weights.Select(x => new SelectListItem() { Selected = x.Id == model.CurrentPackage.WeightMeasureId, Text = x.Name, Value = x.Id.ToString() }).ToList();
                */
            }
        }
        #endregion

        #region Order list

        public ActionResult Index()
        {
            return RedirectToAction("List");
        }

        public ActionResult List()
        {
            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageOrders);
            if (curManagedStoreId == 0)
            {
                return AccessDeniedView();
            }

            var model = new OrderListModel();
            model.AvailableOrderStatuses = OrderStatus.Pending.ToSelectList(false).ToList();
            model.AvailableOrderStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });

            model.AvailablePaymentStatuses = PaymentStatus.Pending.ToSelectList(false).ToList();
            model.AvailablePaymentStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });

            model.AvailableDistricts = _logisticsService.GetAllDistricts()
                .Select(x => new SelectListItem() { Value = x.Id.ToString(), Text = x.Name, Selected = false })
                .ToList();
            model.AvailableDistricts.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });

            model.AvailableShippingStatuses = ShippingStatus.NotYetShipped.ToSelectList(false).ToList();
            model.AvailableShippingStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });

            model.AvailableOrderKinds = OrderKind.Commerce.ToSelectList(false).ToList();
            model.AvailableOrderKinds.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });

            //stores
            model.SearchStoreId = curManagedStoreId;
            model.CanManageAllStore = curManagedStoreId == Store.AllStoreReservedId;

            var managedStore = _workContext.CurrentCustomer.ManagedStore;
            model.SearchStore = managedStore != null ? managedStore.Name : null;

            if (!model.CanManageAllStore)
            {
                model.ShippingStatusId = (int)ShippingStatus.NotYetShipped;
                model.OrderKindId = (int)OrderKind.Commerce;
            }

            if (!model.CanManageAllStore)
            {
                //Remove Edmonton from store list for store manager
                model.AvailableDistricts.RemoveAt(3);
                return View("StoreOrderList", model);
            }
            else
            {
                return View("List", model);
            }
        }

        //[GridAction(EnableCustomBinding = true)]
        public ActionResult OrderList(GridCommand command, OrderListModel model)
        {
            //because there is something wrong i can not found in the Grid plugin so the pagesize......
            command.PageSize = EngineContext.Current.Resolve<Nop.Core.Domain.Common.AdminAreaSettings>().GridPageSize;
            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageOrders);
            if (curManagedStoreId == 0 || ((curManagedStoreId != Store.AllStoreReservedId && model.SearchStoreId != curManagedStoreId)))
            {
                return AccessDeniedView();
            }

            DateTime? startDateValue = (model.StartDate == null) ? null
                            : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.StartDate.Value, _dateTimeHelper.CurrentTimeZone);

            DateTime? endDateValue = (model.EndDate == null) ? null
                            : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.EndDate.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1);

            DateTime? pickUpTime = (model.PickUpTime == null) ? null :
                (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.PickUpTime.Value, _dateTimeHelper.CurrentTimeZone);

            OrderStatus? orderStatus = model.OrderStatusId > 0 ? (OrderStatus?)(model.OrderStatusId) : null;
            PaymentStatus[] paymentStatus = model.PaymentStatusId > 0 ? new PaymentStatus[] { (PaymentStatus)(model.PaymentStatusId) } : null;
            ShippingStatus? shippingStatus = model.ShippingStatusId > 0 ? (ShippingStatus?)(model.ShippingStatusId) : null;
            OrderKind? orderKind = model.OrderKindId > 0 ? (OrderKind?)(model.OrderKindId) : null;
            int? districtId = model.DistrictId > 0 ? (int?)model.DistrictId : null;

            //load orders
            var orders = _orderService.SearchOrders(
                startDateValue,
                endDateValue,
                orderStatus,
                paymentStatus,
                shippingStatus,
                model.CustomerEmail,
                model.OrderGuid,
                command.Page - 1,
                command.PageSize,
                null,
                model.SearchStoreId,
                districtId,
                orderKind, false,
                model.GiftCardUsed ? (bool?)true : null, pickUpTime);
            var allTimeWindows = _logisticsService.GetAllShippingTimeWindows();
            var gridModel = new GridModel<OrderModel>
            {
                Data = orders.Select(x =>
                {
                    var createdOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc);
                    string shippingRateComputationMethodSystemName = "";
                    if (!String.IsNullOrEmpty(x.ShippingRateComputationMethodSystemName))
                        shippingRateComputationMethodSystemName = _shippingService.LoadShippingRateComputationMethodBySystemName(x.ShippingRateComputationMethodSystemName).PluginDescriptor.FriendlyName;
                    var strKindStatus = "";
                    switch (x.OrderKind)
                    {
                        case OrderKind.Commerce:
                            strKindStatus = "Commerce";
                            break;
                        case OrderKind.CommerceMembership:
                            strKindStatus = "CommerceMembership";
                            break;
                        case OrderKind.Express:
                            strKindStatus = "Express";
                            break;
                        case OrderKind.ExpressMembership:
                            strKindStatus = "ExpressMembership";
                            break;
                        case OrderKind.Reserve:
                            strKindStatus = "Reserve";
                            break;
                    }
                    var orderModel = new OrderModel()
                    {
                        Id = x.Id,
                        OrderTotal = _priceFormatter.FormatPrice(x.OrderTotal - x.RefundedAmount, true, false),
                        OrderStatus = x.OrderStatus.GetLocalizedEnum(_localizationService, _workContext),
                        PaymentStatus = x.PaymentStatus.GetLocalizedEnum(_localizationService, _workContext),
                        ShippingStatus = x.ShippingStatus.GetLocalizedEnum(_localizationService, _workContext),
                        OrderKindStatus = strKindStatus,
                        ShippingStatusId = (int)x.ShippingStatus,
                        CustomerEmail = x.BillingAddress.Email,
                        CreatedOn = createdOn,
                        CreatedOnStr = createdOn.ToString("g"),
                        ReadyTime = null,
                        ReadyTimeStr = String.Empty,
                        HasGiftcardHistory = x.GiftCardUsageHistory.Count > 0 ? true : false,
                        ShippingRateComputationMethodSystemName = shippingRateComputationMethodSystemName,
                        ShipmentDateString = x.ShipmentDate.HasValue ? x.ShipmentDate.Value.ToShortDateString() : "",
                        ShipmentTimeRangeString = x.ShippingTimeWindowsId > 0 ? allTimeWindows.First(window => window.Id == x.ShippingTimeWindowsId).Description : null
                    };
                    if (x.ShippingStatus == ShippingStatus.ReadyForPickUp)
                    {
                        var readyTimes = _orderService.GetReadyTimesByOrder(x.Id);
                        orderModel.ReadyTime = readyTimes.Count > 0 ? (DateTime?)readyTimes.Last() : null;
                        orderModel.ReadyTimeStr = orderModel.ReadyTime != null ? _dateTimeHelper.ConvertToUserTime(orderModel.ReadyTime.Value, DateTimeKind.Utc).ToString("g", new CultureInfo(_workContext.WorkingLanguage.LanguageCulture)) : String.Empty;
                    }

                    return orderModel;
                }),
                Total = orders.TotalCount
            };

            //summary report
            //implemented as a workaround described here: http://www.telerik.com/community/forums/aspnet-mvc/grid/gridmodel-aggregates-how-to-use.aspx

            //var reportSummary = _orderReportService.GetOrderAverageReportLine
            //    (orderStatus, paymentStatus, shippingStatus, startDateValue, endDateValue, model.CustomerEmail, model.SearchStoreId, false, districtId, (int?)orderKind);

            //var reportSummary = new OrderAverageReportLine();

            ////var profit = _orderReportService.ProfitReport
            ////(orderStatus, paymentStatus, shippingStatus, startDateValue, endDateValue, model.CustomerEmail, model.SearchStoreId, districtId, (int?)orderKind);
            //var orderTaxSurchargeList = _orderReportService.TaxReport
            //    (orderStatus, paymentStatus, shippingStatus, startDateValue, endDateValue, model.CustomerEmail, model.SearchStoreId, false, districtId, (int?)curManagedStoreId).ToList();
            //var taxList = new List<String>();

            //foreach (var tax in orderTaxSurchargeList)
            //{
            //    string taxRate = _priceFormatter.FormatTaxRate(tax, false, true);
            //    string taxValue = _priceFormatter.FormatPrice(tax.Value, true, false);
            //    string taxStr = String.Format(_localizationService.GetResource("Admin.Order.List.Tax.DifferentKinds", _workContext.WorkingLanguage.Id),
            //                taxRate, taxValue);
            //    taxList.Add(taxStr);
            //}
            ////need ordertax without taxrate?
            //if (reportSummary.SumTax > 0 && curManagedStoreId == Store.AllStoreReservedId)
            //{
            //    var totalOrderTaxWithProvinceName = orderTaxSurchargeList.Sum(x => x.Value);
            //    if (reportSummary.SumTax > totalOrderTaxWithProvinceName)
            //    {
            //        var otherTax = String.Format(_localizationService.GetResource("Admin.Order.List.Tax.DifferentKinds", _workContext.WorkingLanguage.Id),
            //                _localizationService.GetResource("Admin.Order.List.Tax.OtherTax"), _priceFormatter.FormatPrice(reportSummary.SumTax - totalOrderTaxWithProvinceName, true, false));
            //        taxList.Add(otherTax);
            //    }
            //}

            //var aggregator = new OrderModel()
            //{
            //    //aggregatorprofit = _priceFormatter.FormatPrice(profit, true, false),
            //    //aggregatorsubtotalexcltax = _priceFormatter.FormatPrice(reportSummary.SumOrderSubtotalExclTax, true, false),
            //    //aggregatorfuelsurcharge = _priceFormatter.FormatPrice(reportSummary.SumFuelSurcharge, true, false),
            //    //aggregatortax = taxList,
            //    //aggregatortotal = _priceFormatter.FormatPrice(reportSummary.SumOrders, true, false)
            //    aggregatortotal = _priceFormatter.FormatPrice(orders.Sum(o => o.OrderTotal), true, false),
            //    aggregatorsubtotalexcltax = _priceFormatter.FormatPrice(orders.Sum(o => o.OrderSubtotalExclTax), true, false),
            //    aggregatortax = taxList,
            //    //aggregatorfuelsurcharge = _priceFormatter.FormatPrice(reportSummary.SumFuelSurcharge, true, false),
            //};
            gridModel.Aggregates = TotalFilterSubOrder(orderStatus, paymentStatus, shippingStatus, startDateValue, endDateValue, model, districtId, orderKind, curManagedStoreId);

            return new JsonResult
            {
                Data = gridModel
            };
        }

        private OrderModel TotalFilterSubOrder(OrderStatus? orderStatus, PaymentStatus[] paymentStatus, ShippingStatus? shippingStatus, DateTime? startDateValue, DateTime? endDateValue, OrderListModel model, int? districtId, OrderKind? orderKind, int? curManagedStoreId)
        {
            var reportSummary = new OrderAverageReportLine();

            var orderTaxSurchargeList = _orderReportService.TaxReport(orderStatus, paymentStatus, shippingStatus, startDateValue, endDateValue, model.CustomerEmail, model.OrderGuid, model.SearchStoreId, false, districtId, (int?)orderKind).ToList();


            var taxDic = new Dictionary<string, string>();
            taxDic.Add("GST", string.Empty);
            taxDic.Add("PST(BC)", string.Empty);
            taxDic.Add("GST(BC)", string.Empty);
            var taxList = new List<String>();
            foreach (var tax in orderTaxSurchargeList)
            {
                var taxRate = _priceFormatter.FormatTaxRate(tax, false, true);
                //if (taxRate.Equals("GST(BC)")) continue;

                var taxValue = _priceFormatter.FormatPrice(tax.Value, true, false);
                var taxStr = String.Format(_localizationService.GetResource("Admin.Order.List.Tax.DifferentKinds", _workContext.WorkingLanguage.Id),
                            taxRate, taxValue);
                if (taxDic.ContainsKey(taxRate))
                {
                    taxDic[taxRate] = taxStr;
                }

                taxList.Add(taxStr);
            }

            //need ordertax without taxrate?
            if (curManagedStoreId == Store.AllStoreReservedId)
            {
                var totalOrderTaxWithProvinceName = orderTaxSurchargeList.Sum(x => x.Value);
                if (reportSummary.SumTax > totalOrderTaxWithProvinceName)
                {
                    var otherTax = String.Format(_localizationService.GetResource("Admin.Order.List.Tax.DifferentKinds", _workContext.WorkingLanguage.Id),
                            _localizationService.GetResource("Admin.Order.List.Tax.OtherTax"), _priceFormatter.FormatPrice(reportSummary.SumTax - totalOrderTaxWithProvinceName, true, false));
                    taxList.Add(otherTax);
                }
            }

            var orders = _orderService.QSearchOrders(
                startDateValue,
                endDateValue,
                orderStatus,
                paymentStatus,
                shippingStatus,
                model.CustomerEmail,
                model.OrderGuid,
                null,
                model.SearchStoreId,
                districtId,
                orderKind, false,
                model.GiftCardUsed ? (bool?)true : null);

            var aggregator = new OrderModel()
            {
                aggregatortotal = orders.Count() > 0 ? _priceFormatter.FormatPrice(orders.Sum(o => o.OrderTotal), true, false) : _priceFormatter.FormatPrice(Decimal.Zero, true, false),
                aggregatorsubtotalexcltax = orders.Count() > 0 ? _priceFormatter.FormatPrice(orders.Sum(o => o.OrderSubtotalExclTax), true, false) : _priceFormatter.FormatPrice(Decimal.Zero, true, false),
                aggregatortax = taxDic.Values.ToList(),
                aggregatorfuelsurcharge = reportSummary.SumFuelSurcharge > 0 ? _priceFormatter.FormatPrice(reportSummary.SumFuelSurcharge, true, false) : _priceFormatter.FormatPrice(Decimal.Zero, true, false)
                //aggregatorsubtotalexcltax = _priceFormatter.FormatPrice(reportSummary.SumOrderSubtotalExclTax, true, false),
                //aggregatorfuelsurcharge = _priceFormatter.FormatPrice(reportSummary.SumFuelSurcharge, true, false),
                //aggregatortax = _priceFormatter.FormatPrice(reportSummary.SumTax, true, false),
                //aggregatortotal = _priceFormatter.FormatPrice(reportSummary.SumOrders, true, false)
            };

            return aggregator;
        }

        [HttpPost, ActionName("List")]
        [FormValueRequired("go-to-order-by-number")]
        public ActionResult GoToOrderId(OrderListModel model)
        {
            var order = _orderService.GetOrderById(model.GoDirectlyToNumber);
            if (order != null)
                return RedirectToAction("Edit", "Order", new { id = order.Id });
            else
                return List();
        }

        #endregion

        #region Export / Import / Print

        public ActionResult ExportXmlAll()
        {
            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageOrders);
            if (curManagedStoreId == 0)
            {
                return AccessDeniedView();
            }

            try
            {
                var orders = _orderService.SearchOrders(null, null, null,
                    null, null, null, null, 0, int.MaxValue, null, curManagedStoreId);

                var xml = _exportManager.ExportOrdersToXml(orders);
                return new XmlDownloadResult(xml, "orders.xml");
            }
            catch (Exception exc)
            {
                ErrorNotification(exc);
                return RedirectToAction("List");
            }
        }

        public ActionResult ExportXmlSelected(string selectedIds)
        {
            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageOrders);
            if (curManagedStoreId == 0)
            {
                return AccessDeniedView();
            }

            var orders = new List<Order>();
            if (selectedIds != null)
            {
                var ids = selectedIds
                    .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                    .Select(x => Convert.ToInt32(x))
                    .ToArray();
                orders.AddRange(_orderService.GetOrdersByIds(ids, curManagedStoreId));
            }

            var xml = _exportManager.ExportOrdersToXml(orders);
            return new XmlDownloadResult(xml, "orders.xml");
        }

        public ActionResult ExportExcelAll(OrderListModel searchModel)
        {
            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageOrders);
            if (curManagedStoreId == 0)
            {
                return AccessDeniedView();
            }

            try
            {
                DateTime? startDateValue = (searchModel.StartDate == null) ? null
                            : (DateTime?)_dateTimeHelper.ConvertToUtcTime(searchModel.StartDate.Value, _dateTimeHelper.CurrentTimeZone);

                DateTime? endDateValue = (searchModel.EndDate == null) ? null
                                : (DateTime?)_dateTimeHelper.ConvertToUtcTime(searchModel.EndDate.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1);

                OrderStatus? orderStatus = searchModel.OrderStatusId > 0 ? (OrderStatus?)(searchModel.OrderStatusId) : null;
                var paymentStatus = searchModel.PaymentStatusId > 0 ? new PaymentStatus[] { (PaymentStatus)(searchModel.PaymentStatusId) } : null;
                ShippingStatus? shippingStatus = searchModel.ShippingStatusId > 0 ? (ShippingStatus?)(searchModel.ShippingStatusId) : null;
                OrderKind? orderKind = searchModel.OrderKindId > 0 ? (OrderKind?)(searchModel.OrderKindId) : null;
                int? districtId = searchModel.DistrictId > 0 ? (int?)searchModel.DistrictId : null;

                IList<Order> orders = _orderService.SearchOrders(
                                                                startDateValue,
                                                                endDateValue,
                                                                orderStatus,
                                                                paymentStatus,
                                                                shippingStatus,
                                                                searchModel.CustomerEmail,
                                                                null,
                                                                0,
                                                                int.MaxValue,
                                                                null,
                                                                searchModel.SearchStoreId,
                                                                districtId,
                                                                orderKind);

                byte[] bytes = null;
                using (var stream = new MemoryStream())
                {
                    _exportManager.ExportOrdersToXlsx(stream, orders);
                    bytes = stream.ToArray();
                }
                return File(bytes, "text/xls", "orders.xlsx");
            }
            catch (Exception exc)
            {
                ErrorNotification(exc);
                return RedirectToAction("List");
            }
        }

        public ActionResult ExportExcelSelected(string selectedIds)
        {
            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageOrders);
            if (curManagedStoreId == 0)
            {
                return AccessDeniedView();
            }

            var orders = new List<Order>();
            if (selectedIds != null)
            {
                var ids = selectedIds
                    .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                    .Select(x => Convert.ToInt32(x))
                    .ToArray();
                orders.AddRange(_orderService.GetOrdersByIds(ids, curManagedStoreId));
            }

            byte[] bytes = null;
            using (var stream = new MemoryStream())
            {
                _exportManager.ExportOrdersToXlsx(stream, orders);
                bytes = stream.ToArray();
            }
            return File(bytes, "text/xls", "orders.xlsx");
        }

        public ActionResult PrintPicking(int orderId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageLogistics) &&
             !_permissionService.Authorize(StandardPermissionProvider.ViewLogisticsRoutes))
                return AccessDeniedView();

            if (orderId <= 0) return RedirectToAction("Edit", new { id = orderId });

            try
            {
                var variansInfo = PreparePackInfoForPrint(orderId);

                if (variansInfo == null) return RedirectToAction("Edit", new { id = orderId });

                byte[] bytes = null;
                using (var stream = new MemoryStream())
                {
                    _pdfService.PrintPickingToPdf(stream, variansInfo, _workContext.WorkingLanguage, orderId);
                    bytes = stream.ToArray();
                }

                return File(bytes, "application/pdf", WaybillFilename.GenerateWaybillFilename(string.Format("{0}-picking-{1}", orderId, DateTime.Now.Date)));
            }
            catch (Exception exc)
            {
                ErrorNotification(exc);
                return RedirectToAction("Edit", new { id = orderId });
            }

            //return null;
        }

        private List<PdfPackagePickingInfo> PreparePackInfoForPrint(int orderId)
        {
            var order = _orderService.GetOrderById(orderId);

            var shipments = order.Shipments;
            if (shipments == null || shipments.Count <= 0) return null;

            var result = new List<PdfPackagePickingInfo>();

            foreach (var shipment in shipments)
            {
                var shipOrderVariants = shipment.ShipmentOrderProductVariants;
                if (shipOrderVariants == null || shipOrderVariants.Count <= 0) continue;
                foreach (var item in shipOrderVariants)
                {
                    var orderProductVarient =
                        _orderService.GetOrderProductVariantById(item.OrderProductVariantId);
                    if (orderProductVarient == null) continue;

                    var variant = _productService.GetProductVariantById(orderProductVarient.ProductVariantId);
                    if (variant == null) continue;
                    result.Add(new PdfPackagePickingInfo()
                    {
                        Quantity = item.Quantity,
                        Variant = variant
                    });
                }
            }
            return result;
        }

        public ActionResult PdfInvoice(int orderId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(orderId);
            var orders = new List<Order> { order };
            byte[] bytes;
            using (var stream = new MemoryStream())
            {
                _pdfService.PrintOrdersToPdf(stream, orders, _workContext.WorkingLanguage);
                bytes = stream.ToArray();
            }
            return File(bytes, "application/pdf", string.Format("order_{0}.pdf", order.Id));
        }

        public ActionResult PdfMessageLabel(int orderId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(orderId);
            if (order == null || string.IsNullOrEmpty(order.CheckoutAttributeDescription)) return RedirectToAction("List");

            byte[] bytes;
            using (var stream = new MemoryStream())
            {
                _pdfService.PrintMessageLabelToPdf(stream, PrepareMessageLabeInfoForPrint(order.CheckoutAttributesXml), _workContext.WorkingLanguage);
                bytes = stream.ToArray();
            }
            return File(bytes, "application/pdf", string.Format("gift_message_{0}.pdf", order.Id));
        }

        public Dictionary<string, string> PrepareMessageLabeInfoForPrint(string checkoutAttributeXml)
        {
            var result = new Dictionary<string, string>();

            var attributes = _checkoutAttributeParser.ParseCheckoutAttributes(checkoutAttributeXml);

            foreach (var item in attributes)
            {
                var name = item.Name.ToLower();
                if (name.Equals(message) || name.Equals(sender) || name.Equals(receiver))
                {
                    var value = _checkoutAttributeParser.ParseValues(checkoutAttributeXml, item.Id);
                    if (value != null && value.Count > 0)
                    {
                        result.Add(name, value[0]);
                    }
                }
            }

            return result;
        }

        #endregion

        #region Order details

        #region Payments and other order workflow

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("cancelorder")]
        public ActionResult CancelOrder(int id)
        {
            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageOrders);
            if (curManagedStoreId == 0)
            {
                return AccessDeniedView();
            }

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            if (curManagedStoreId != Store.AllStoreReservedId && _orderService.GetStoreIdByOrderId(order.Id) != curManagedStoreId)
            {
                return AccessDeniedView();
            }

            try
            {
                _orderProcessingService.CancelOrder(order, order.OrderKind == OrderKind.Commerce);
                _customerActivityService.InsertActivity("CancelOrder", _localizationService.GetResource("ActivityLog.CancelOrder"), order.Id);
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                ViewData["pickerModel"] = PreparePickerModel(model.OrderKind, model.ShippingAddress);
                return View(model);
            }
            catch (Exception exc)
            {
                //error
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                ViewData["pickerModel"] = PreparePickerModel(model.OrderKind, model.ShippingAddress);
                ErrorNotification(exc, false);
                return View(model);
            }
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("captureorder")]
        public ActionResult CaptureOrder(int id)
        {
            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageOrders);
            if (curManagedStoreId == 0)
            {
                return AccessDeniedView();
            }

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            if (curManagedStoreId != Store.AllStoreReservedId && _orderService.GetStoreIdByOrderId(order.Id) != curManagedStoreId)
            {
                return AccessDeniedView();
            }

            try
            {
                var errors = _orderProcessingService.Capture(order);
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                foreach (var error in errors)
                    ErrorNotification(error, false);
                ViewData["pickerModel"] = PreparePickerModel(model.OrderKind, model.ShippingAddress);
                return View(model);
            }
            catch (Exception exc)
            {
                //error
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                ErrorNotification(exc, false);
                ViewData["pickerModel"] = PreparePickerModel(model.OrderKind, model.ShippingAddress);
                return View(model);
            }

        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("markorderaspaid")]
        public ActionResult MarkOrderAsPaid(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            try
            {
                _orderProcessingService.MarkOrderAsPaid(order, false);
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                ViewData["pickerModel"] = PreparePickerModel(model.OrderKind, model.ShippingAddress);
                return View(model);
            }
            catch (Exception exc)
            {
                //error
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                ErrorNotification(exc, false);
                ViewData["pickerModel"] = PreparePickerModel(model.OrderKind, model.ShippingAddress);
                return View(model);
            }
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("refundorder")]
        public ActionResult RefundOrder(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            try
            {
                var errors = _orderProcessingService.Refund(order);
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                foreach (var error in errors)
                    ErrorNotification(error, false);
                ViewData["pickerModel"] = PreparePickerModel(model.OrderKind, model.ShippingAddress);
                return View(model);
            }
            catch (Exception exc)
            {
                //error
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                ErrorNotification(exc, false);
                ViewData["pickerModel"] = PreparePickerModel(model.OrderKind, model.ShippingAddress);
                return View(model);
            }
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("refundorderoffline")]
        public ActionResult RefundOrderOffline(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            try
            {
                _orderProcessingService.RefundOffline(order);
                _invoiceService.UpdateInvoiceStatus(order);
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                ViewData["pickerModel"] = PreparePickerModel(model.OrderKind, model.ShippingAddress);
                return View(model);
            }
            catch (Exception exc)
            {
                //error
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                ErrorNotification(exc, false);
                ViewData["pickerModel"] = PreparePickerModel(model.OrderKind, model.ShippingAddress);
                return View(model);
            }
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("voidorder")]
        public ActionResult VoidOrder(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            try
            {
                var errors = _orderProcessingService.Void(order);
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                foreach (var error in errors)
                    ErrorNotification(error, false);
                ViewData["pickerModel"] = PreparePickerModel(model.OrderKind, model.ShippingAddress);
                return View(model);
            }
            catch (Exception exc)
            {
                //error
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                ErrorNotification(exc, false);
                ViewData["pickerModel"] = PreparePickerModel(model.OrderKind, model.ShippingAddress);
                return View(model);
            }
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("voidorderoffline")]
        public ActionResult VoidOrderOffline(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            try
            {
                _orderProcessingService.VoidOffline(order);
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                ViewData["pickerModel"] = PreparePickerModel(model.OrderKind, model.ShippingAddress);
                return View(model);
            }
            catch (Exception exc)
            {
                //error
                var model = new OrderModel();
                PrepareOrderDetailsModel(model, order);
                ErrorNotification(exc, false);
                ViewData["pickerModel"] = PreparePickerModel(model.OrderKind, model.ShippingAddress);
                return View(model);
            }
        }

        public ActionResult PartiallyRefundOrderPopup(int id, bool online)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            var model = new OrderModel();
            model.Online = online;
            model.Id = order.Id;

            //order product variant
            foreach (var opv in order.OrderProductVariants)
            {
                var opvModel = new OrderModel.OrderProductVariantModel()
                {
                    Id = opv.Id,
                    ProductVariantId = opv.ProductVariantId,
                    UnitPriceExclTax = opv.UnitPriceExclTax.ToString(),
                    Quantity = opv.Quantity,
                    SubTotalExclTax = opv.PriceExclTax.ToString(),
                    DiscountExclTax = opv.DiscountAmountExclTax.ToString(),
                    IsRefund = opv.IsRefund == 1
                };

                if (!String.IsNullOrEmpty(opv.ProductVariant.GetLocalized(x => x.Name)))
                    opvModel.FullProductName = string.Format("{0} ({1})", opv.ProductVariant.Product.GetLocalized(x => x.Name), opv.ProductVariant.GetLocalized(x => x.Name));
                else
                    opvModel.FullProductName = opv.ProductVariant.Product.GetLocalized(x => x.Name);

                model.Items.Add(opvModel);
            }
            return View(model);
        }

        [HttpPost, ActionName("PartiallyRefundOrderPopup")]
        [ValidateInput(false)]
        public ActionResult PartiallyRefundOrderSubmit(OrderModel model, FormCollection form)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            try
            {
                var order = _orderService.GetOrderById(model.Id);
                if (order == null)
                    //No order found with the specified id
                    return RedirectToAction("List");

                decimal amountToRefund = 0;
                int count = 0;
                foreach (var opv in order.OrderProductVariants)
                {
                    int quantity = 0; //parse quantity
                    foreach (string formKey in form.AllKeys)
                    {
                        if (formKey.Equals(string.Format("quantity{0}", opv.Id), StringComparison.InvariantCultureIgnoreCase))
                        {
                            int.TryParse(form[formKey], out quantity);
                            break;
                        }
                    }

                    if (quantity > 0)
                    {
                        amountToRefund += opv.UnitPriceInclTax * quantity;
                        opv.IsRefund = 1;
                        opv.RefundQuantity = quantity;
                        count++;
                    }
                    //else
                    //{
                    //    opv.IsRefund = 0;
                    //    opv.RefundQuantity = 0;
                    //}
                }

                if (count > 0)
                {
                    var errors = new List<string>();
                    if (model.Online)
                        errors = _orderProcessingService.PartiallyRefund(order, amountToRefund).ToList();
                    else
                        _orderProcessingService.PartiallyRefundOffline(order, amountToRefund);

                    if (errors.Count == 0)
                    {
                        //success
                        _orderService.UpdateOrder(order);
                        return RedirectToAction("Edit", new { id = model.Id });
                    }
                    else
                    {
                        foreach (var error in errors)
                            ErrorNotification(error, false);
                        return RedirectToAction("Edit", new { id = model.Id });
                    }
                }

                ErrorNotification(_localizationService.GetResource("ReturnRequests.NoItemsSubmitted"));
                return RedirectToAction("Edit", new { id = model.Id });
            }
            catch (Exception exc)
            {
                ErrorNotification(exc);
                return RedirectToAction("Edit", new { id = model.Id });
            }
        }

        #endregion

        #region Edit, delete

        public ActionResult Edit(int id)
        {
            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageOrders);
            if (curManagedStoreId == 0 || (curManagedStoreId != Store.AllStoreReservedId && _orderService.GetStoreIdByOrderId(id) != curManagedStoreId))
            {
                return AccessDeniedView();
            }

            var order = _orderService.GetOrderById(id);
            if (order == null || order.Deleted)
                //No order found with the specified id
                return RedirectToAction("List");

            var model = new OrderModel();
            PrepareOrderDetailsModel(model, order);
            model.IsAdmin = _permissionService.Authorize(StandardPermissionProvider.ManageCatalog);
            model.ShipmentPackageModel.DimensionMeasureId = _measureSettings.BaseDimensionId;
            model.ShipmentPackageModel.WeightMeasureId = _measureSettings.BaseWeightId;

            //if (curManagedStoreId != Store.AllStoreReservedId)
            //{
            //    return View("EditForStoreManager", model);
            //}
            model.BoundInvoiceId = order.InvoiceId;
            model.HasThirdPartyShippingOption = false;
            if (order.ThirdPartyShippingOptions != null && order.ThirdPartyShippingOptions.Count > 0)
                model.HasThirdPartyShippingOption = true;


            var pickerModel = PreparePickerModel(model.OrderKind, model.ShippingAddress);
            if (pickerModel == null)
            {
                return RedirectToAction("List");
            }
            ViewData["pickerModel"] = pickerModel;

            return View(model);
        }

        private DatePickerModel PreparePickerModel(OrderKind orderKind, AddressModel shippingAddress)
        {
            if (shippingAddress == null || shippingAddress.Type == null) return null;

            Address shipAddress = _addressService.GetAddressById(shippingAddress.Id);
            var type = shipAddress.Type == null ? AddressType.Business : shipAddress.Type.Value;
            var zipPostalCode = shipAddress.ZipPostalCode;

            return _logisticsService.GetPickerModel((int)orderKind, type, zipPostalCode);
        }

        [HttpPost]
        public ActionResult Delete(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            _orderProcessingService.DeleteOrder(order);

            //get metched order and delete
            var metchedOrder = GetMetchedOrder(order);
            if (metchedOrder != null)
            {
                if (order.BoundOrderId != null && order.OrderKindId == (int)OrderKind.Express)
                {
                    metchedOrder.ShippingStatus = ShippingStatus.NotYetReady;

                    DeleteOrderShipmentPkg(metchedOrder);

                    _orderService.UpdateOrder(metchedOrder);
                }
                else
                {
                    _orderProcessingService.DeleteOrder(metchedOrder);
                }
            }

            return RedirectToAction("List");
        }

        /// <summary>
        /// Get matched e-commerce order or matched Express order
        /// </summary>
        /// <param name="order">order</param>
        /// <returns>order</returns>
        private Order GetMetchedOrder(Order order)
        {
            if (order == null || order.Id <= 0) return null;

            switch (order.OrderKind)
            {
                case OrderKind.Commerce:
                    return _orderService.GetOrderByBoundOrderId(order.Id);
                case OrderKind.Express:
                    if (order.BoundOrderId != null && order.BoundOrderId > 0)
                    {
                        return _orderService.GetOrderById((int)order.BoundOrderId);
                    }
                    break;
            }

            return null;
        }

        private void DeleteOrderShipmentPkg(Order order)
        {
            if (order == null)
                throw new ArgumentException("fail to  delete order ");

            var shipments = order.Shipments;
            if (shipments == null || shipments.Count <= 0) return;

            var shipmentCollection = shipments.ToList();
            foreach (var shipment in shipmentCollection)
            {
                if (shipment.PackageId.HasValue)
                {
                    _logisticsService.DeleteLogisticsPackage(shipment.PackageId.Value);
                }

                var spv = shipment.ShipmentOrderProductVariants;

                if (spv == null || spv.Count <= 0) continue;

                var spvCollection = spv.ToList();
                foreach (var item in spvCollection)
                {
                    _shipmentService.DeleteShipmentOrderProductVariant(item);
                }

                _shipmentService.DeleteShipment(shipment);
            }
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("btnSaveCC")]
        public ActionResult EditCreditCardInfo(int id, OrderModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            if (order.AllowStoringCreditCardNumber)
            {
                string cardType = model.CardType;
                string cardName = model.CardName;
                string cardNumber = model.CardNumber;
                string cardCvv2 = model.CardCvv2;
                string cardExpirationMonth = model.CardExpirationMonth;
                string cardExpirationYear = model.CardExpirationYear;

                order.CardType = _encryptionService.EncryptText(cardType);
                order.CardName = _encryptionService.EncryptText(cardName);
                order.CardNumber = _encryptionService.EncryptText(cardNumber);
                order.MaskedCreditCardNumber = _encryptionService.EncryptText(_paymentService.GetMaskedCreditCardNumber(cardNumber));
                order.CardCvv2 = _encryptionService.EncryptText(cardCvv2);
                order.CardExpirationMonth = _encryptionService.EncryptText(cardExpirationMonth);
                order.CardExpirationYear = _encryptionService.EncryptText(cardExpirationYear);
                _orderService.UpdateOrder(order);
            }

            PrepareOrderDetailsModel(model, order);
            ViewData["pickerModel"] = PreparePickerModel(model.OrderKind, model.ShippingAddress);
            return View(model);
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("btnSaveOrderTotals")]
        public ActionResult EditOrderTotals(int id, OrderModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            order.OrderSubtotalInclTax = model.OrderSubtotalInclTaxValue;
            order.OrderSubtotalExclTax = model.OrderSubtotalExclTaxValue;
            order.OrderSubTotalDiscountInclTax = model.OrderSubTotalDiscountInclTaxValue;
            order.OrderSubTotalDiscountExclTax = model.OrderSubTotalDiscountExclTaxValue;
            order.OrderShippingInclTax = model.OrderShippingInclTaxValue;
            order.OrderShippingExclTax = model.OrderShippingExclTaxValue;
            order.PaymentMethodAdditionalFeeInclTax = model.PaymentMethodAdditionalFeeInclTaxValue;
            order.PaymentMethodAdditionalFeeExclTax = model.PaymentMethodAdditionalFeeExclTaxValue;
            order.TaxRates = model.TaxRatesValue;
            order.OrderTax = model.TaxValue;
            order.OrderDiscount = model.OrderTotalDiscountValue;
            order.OrderTotal = model.OrderTotalValue;
            _orderService.UpdateOrder(order);

            //update Surcharge
            var surCharges = _taxService.GetSurchargesByOrder(order);
            if (surCharges != null && surCharges.Count > 0)
            {
                for (var i = 0; i < surCharges.Count; i++)
                {
                    var item = surCharges[i];
                    item.Value = (item.PercentageOrFee / 100) * order.OrderSubtotalExclTax;
                }
                _taxService.UpdateSurcharge(surCharges);
            }

            //update invoice if included
            if (order.InvoiceId.HasValue)
            {
                var invoice = _invoiceService.GetInvoiceById(order.InvoiceId.Value);
                UpdateInvoiceAfterOrderChange(invoice, order.Id);
            }
            PrepareOrderDetailsModel(model, order);

            ViewData["pickerModel"] = PreparePickerModel(model.OrderKind, model.ShippingAddress);

            return View(model);
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired(FormValueRequirement.StartsWith, "btnSaveOpv")]
        [ValidateInput(false)]
        public ActionResult EditOrderProductVariant(int id, FormCollection form)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            ViewData["selectedTab"] = "products";

            //get order product variant identifier
            int opvId = 0;
            foreach (var formValue in form.AllKeys)
                if (formValue.StartsWith("btnSaveOpv", StringComparison.InvariantCultureIgnoreCase))
                    opvId = Convert.ToInt32(formValue.Substring("btnSaveOpv".Length));

            var orderProductVariant = order.OrderProductVariants.Where(x => x.Id == opvId).FirstOrDefault();
            if (orderProductVariant == null)
                throw new ArgumentException("No order product variant found with the specified id");


            decimal unitPriceInclTax, unitPriceExclTax, discountInclTax, discountExclTax, priceInclTax, priceExclTax;
            int quantity;
            if (!decimal.TryParse(form["pvUnitPriceInclTax" + opvId], out unitPriceInclTax))
                unitPriceInclTax = orderProductVariant.UnitPriceInclTax;
            if (!decimal.TryParse(form["pvUnitPriceExclTax" + opvId], out unitPriceExclTax))
                unitPriceExclTax = orderProductVariant.UnitPriceExclTax;
            if (!int.TryParse(form["pvQuantity" + opvId], out quantity))
                quantity = orderProductVariant.Quantity;
            if (!decimal.TryParse(form["pvDiscountInclTax" + opvId], out discountInclTax))
                discountInclTax = orderProductVariant.DiscountAmountInclTax;
            if (!decimal.TryParse(form["pvDiscountExclTax" + opvId], out discountExclTax))
                discountExclTax = orderProductVariant.DiscountAmountExclTax;
            if (!decimal.TryParse(form["pvPriceInclTax" + opvId], out priceInclTax))
                priceInclTax = orderProductVariant.PriceInclTax;
            if (!decimal.TryParse(form["pvPriceExclTax" + opvId], out priceExclTax))
                priceExclTax = orderProductVariant.PriceExclTax;

            if (quantity > 0)
            {
                orderProductVariant.UnitPriceInclTax = unitPriceInclTax;
                orderProductVariant.UnitPriceExclTax = unitPriceExclTax;
                orderProductVariant.Quantity = quantity;
                orderProductVariant.DiscountAmountInclTax = discountInclTax;
                orderProductVariant.DiscountAmountExclTax = discountExclTax;
                orderProductVariant.PriceInclTax = priceInclTax;
                orderProductVariant.PriceExclTax = priceExclTax;
                _orderService.UpdateOrder(order);
            }
            else
            {
                _orderService.DeleteOrderProductVariant(orderProductVariant);
            }

            var model = new OrderModel();
            PrepareOrderDetailsModel(model, order);
            ViewData["pickerModel"] = PreparePickerModel(model.OrderKind, model.ShippingAddress);
            return View(model);
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired(FormValueRequirement.StartsWith, "btnDeleteOpv")]
        [ValidateInput(false)]
        public ActionResult DeleteOrderProductVariant(int id, FormCollection form)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            ViewData["selectedTab"] = "products";

            //get order product variant identifier
            int opvId = 0;
            foreach (var formValue in form.AllKeys)
                if (formValue.StartsWith("btnDeleteOpv", StringComparison.InvariantCultureIgnoreCase))
                    opvId = Convert.ToInt32(formValue.Substring("btnDeleteOpv".Length));

            var orderProductVariant = order.OrderProductVariants.Where(x => x.Id == opvId).FirstOrDefault();
            if (orderProductVariant == null)
                throw new ArgumentException("No order product variant found with the specified id");

            if (_taxService.DeleteOPVRelatedSurcharge(orderProductVariant.Id))
                SuccessNotification("Successfully delete related surcharge of this opv");

            _orderService.DeleteOrderProductVariant(orderProductVariant);

            var model = new OrderModel();
            PrepareOrderDetailsModel(model, order);
            ViewData["pickerModel"] = PreparePickerModel(model.OrderKind, model.ShippingAddress);
            return View(model);
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired(FormValueRequirement.StartsWith, "btnResetDownloadCount")]
        [ValidateInput(false)]
        public ActionResult ResetDownloadCount(int id, FormCollection form)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            ViewData["selectedTab"] = "products";

            //get order product variant identifier
            int opvId = 0;
            foreach (var formValue in form.AllKeys)
                if (formValue.StartsWith("btnResetDownloadCount", StringComparison.InvariantCultureIgnoreCase))
                    opvId = Convert.ToInt32(formValue.Substring("btnResetDownloadCount".Length));

            var orderProductVariant = order.OrderProductVariants.Where(x => x.Id == opvId).FirstOrDefault();
            if (orderProductVariant == null)
                throw new ArgumentException("No order product variant found with the specified id");

            orderProductVariant.DownloadCount = 0;
            _orderService.UpdateOrder(order);

            var model = new OrderModel();
            PrepareOrderDetailsModel(model, order);
            ViewData["pickerModel"] = PreparePickerModel(model.OrderKind, model.ShippingAddress);
            return View(model);
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired(FormValueRequirement.StartsWith, "btnPvActivateDownload")]
        [ValidateInput(false)]
        public ActionResult ActivateDownloadOrderProductVariant(int id, FormCollection form)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            ViewData["selectedTab"] = "products";

            //get order product variant identifier
            int opvId = 0;
            foreach (var formValue in form.AllKeys)
                if (formValue.StartsWith("btnPvActivateDownload", StringComparison.InvariantCultureIgnoreCase))
                    opvId = Convert.ToInt32(formValue.Substring("btnPvActivateDownload".Length));

            var orderProductVariant = order.OrderProductVariants.Where(x => x.Id == opvId).FirstOrDefault();
            if (orderProductVariant == null)
                throw new ArgumentException("No order product variant found with the specified id");

            orderProductVariant.IsDownloadActivated = !orderProductVariant.IsDownloadActivated;
            _orderService.UpdateOrder(order);

            var model = new OrderModel();
            PrepareOrderDetailsModel(model, order);
            ViewData["pickerModel"] = PreparePickerModel(model.OrderKind, model.ShippingAddress);
            return View(model);
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult ThirdPartyShippingOptionList(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            var shippingOptionModels = new List<OrderModel.ThirdPartyShippingOption>();
            foreach (var shippingOption in order.ThirdPartyShippingOptions
                .OrderByDescending(on => on.CreatedOnUTC))
            {
                shippingOptionModels.Add(new OrderModel.ThirdPartyShippingOption()
                {
                    CustomerEmail = shippingOption.CustomerEmail,
                    BillingAddress = shippingOption.BillingAddress,
                    ShippingAddress = shippingOption.ShippingAddress,
                    Name = shippingOption.Name,
                    ShippingRateComputationMethodSystemName = shippingOption.ShippingRateComputationMethodSystemName,
                    Rate = shippingOption.Rate,
                    PackageCount = shippingOption.PackageCount,
                    CreatedOn = _dateTimeHelper.ConvertToUserTime(shippingOption.CreatedOnUTC, DateTimeKind.Utc)
                });
            }

            var model = new GridModel<OrderModel.ThirdPartyShippingOption>
            {
                Data = shippingOptionModels,
                Total = shippingOptionModels.Count
            };

            return new JsonResult
            {
                Data = model
            };
        }

        public ActionResult UploadLicenseFilePopup(int id, int opvId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(id);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            var orderProductVariant = order.OrderProductVariants.Where(x => x.Id == opvId).FirstOrDefault();
            if (orderProductVariant == null)
                throw new ArgumentException("No order product variant found with the specified id");

            if (!orderProductVariant.ProductVariant.IsDownload)
                throw new ArgumentException("Product variant is not downloadable");

            var model = new OrderModel.UploadLicenseModel()
            {
                LicenseDownloadId = orderProductVariant.LicenseDownloadId.HasValue ? orderProductVariant.LicenseDownloadId.Value : 0,
                OrderId = order.Id,
                OrderProductVariantId = orderProductVariant.Id
            };

            return View(model);
        }

        [HttpPost]
        [FormValueRequired("uploadlicense")]
        public ActionResult UploadLicenseFilePopup(string btnId, string formId, OrderModel.UploadLicenseModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(model.OrderId);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            var orderProductVariant = order.OrderProductVariants.Where(x => x.Id == model.OrderProductVariantId).FirstOrDefault();
            if (orderProductVariant == null)
                throw new ArgumentException("No order product variant found with the specified id");

            //attach license
            if (model.LicenseDownloadId > 0)
                orderProductVariant.LicenseDownloadId = model.LicenseDownloadId;
            else
                orderProductVariant.LicenseDownloadId = null;
            _orderService.UpdateOrder(order);

            //success
            ViewBag.RefreshPage = true;
            ViewBag.btnId = btnId;
            ViewBag.formId = formId;

            return View(model);
        }

        [HttpPost, ActionName("UploadLicenseFilePopup")]
        [FormValueRequired("deletelicense")]
        public ActionResult DeleteLicenseFilePopup(string btnId, string formId, OrderModel.UploadLicenseModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(model.OrderId);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            var orderProductVariant = order.OrderProductVariants.Where(x => x.Id == model.OrderProductVariantId).FirstOrDefault();
            if (orderProductVariant == null)
                throw new ArgumentException("No order product variant found with the specified id");

            //attach license
            orderProductVariant.LicenseDownloadId = null;
            _orderService.UpdateOrder(order);

            //success
            ViewBag.RefreshPage = true;
            ViewBag.btnId = btnId;
            ViewBag.formId = formId;

            return View(model);
        }

        public ActionResult AddProductToOrder(int orderId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var model = new OrderModel.AddOrderProductModel();
            model.OrderId = orderId;
            //categories
            model.AvailableCategories.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            foreach (var c in _categoryService.GetAllCategories(showHidden: true))
                model.AvailableCategories.Add(new SelectListItem() { Text = c.GetCategoryNameWithPrefix(_categoryService), Value = c.Id.ToString() });

            //manufacturers
            model.AvailableManufacturers.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            foreach (var m in _manufacturerService.GetAllManufacturers(true))
                model.AvailableManufacturers.Add(new SelectListItem() { Text = m.Name, Value = m.Id.ToString() });

            return View(model);
        }

        [HttpPost, GridAction(EnableCustomBinding = true)]
        public ActionResult AddProductToOrder(GridCommand command, OrderModel.AddOrderProductModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var gridModel = new GridModel();
            var productVariants = _productService.SearchProductVariants(model.SearchCategoryId,
                model.SearchManufacturerId, model.SearchProductName, false,
                command.Page - 1, command.PageSize, true);
            gridModel.Data = productVariants.Select(x =>
            {
                var productVariantModel = new OrderModel.AddOrderProductModel.ProductVariantLineModel()
                {
                    Id = x.Id,
                    Name = x.FullProductName,
                    Sku = x.Sku,
                };

                return productVariantModel;
            });
            gridModel.Total = productVariants.TotalCount;
            return new JsonResult
            {
                Data = gridModel
            };
        }

        public ActionResult AddProductToOrderDetails(int orderId, int productVariantId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var model = PrepareAddProductToOrderModel(orderId, productVariantId);
            return View(model);
        }

        [HttpPost]
        public ActionResult AddProductToOrderDetails(int orderId, int productVariantId, FormCollection form)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(orderId);
            var productVariant = _productService.GetProductVariantById(productVariantId);
            //save order item

            //basic properties
            var unitPriceInclTax = decimal.Zero;
            decimal.TryParse(form["UnitPriceInclTax"], out unitPriceInclTax);
            var unitPriceExclTax = decimal.Zero;
            decimal.TryParse(form["UnitPriceExclTax"], out unitPriceExclTax);
            var quantity = 1;
            int.TryParse(form["Quantity"], out quantity);
            var priceInclTax = decimal.Zero;
            decimal.TryParse(form["SubTotalInclTax"], out priceInclTax);
            var priceExclTax = decimal.Zero;
            decimal.TryParse(form["SubTotalExclTax"], out priceExclTax);

            //attributes
            //warnings
            var warnings = new List<string>();
            string attributes = "";

            #region Product attributes
            string selectedAttributes = string.Empty;
            var productVariantAttributes = _productAttributeService.GetProductVariantAttributesByProductVariantId(productVariant.Id);
            foreach (var attribute in productVariantAttributes)
            {
                string controlId = string.Format("product_attribute_{0}_{1}", attribute.ProductAttributeId, attribute.Id);
                switch (attribute.AttributeControlType)
                {
                    case AttributeControlType.DropdownList:
                        {
                            var ddlAttributes = form[controlId];
                            if (!String.IsNullOrEmpty(ddlAttributes))
                            {
                                int selectedAttributeId = int.Parse(ddlAttributes);
                                if (selectedAttributeId > 0)
                                    selectedAttributes = _productAttributeParser.AddProductAttribute(selectedAttributes,
                                        attribute, selectedAttributeId.ToString());
                            }
                        }
                        break;
                    case AttributeControlType.RadioList:
                        {
                            var rblAttributes = form[controlId];
                            if (!String.IsNullOrEmpty(rblAttributes))
                            {
                                int selectedAttributeId = int.Parse(rblAttributes);
                                if (selectedAttributeId > 0)
                                    selectedAttributes = _productAttributeParser.AddProductAttribute(selectedAttributes,
                                        attribute, selectedAttributeId.ToString());
                            }
                        }
                        break;
                    case AttributeControlType.Checkboxes:
                        {
                            var cblAttributes = form[controlId];
                            if (!String.IsNullOrEmpty(cblAttributes))
                            {
                                foreach (var item in cblAttributes.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
                                {
                                    int selectedAttributeId = int.Parse(item);
                                    if (selectedAttributeId > 0)
                                        selectedAttributes = _productAttributeParser.AddProductAttribute(selectedAttributes,
                                            attribute, selectedAttributeId.ToString());
                                }
                            }
                        }
                        break;
                    case AttributeControlType.TextBox:
                        {
                            var txtAttribute = form[controlId];
                            if (!String.IsNullOrEmpty(txtAttribute))
                            {
                                string enteredText = txtAttribute.Trim();
                                selectedAttributes = _productAttributeParser.AddProductAttribute(selectedAttributes,
                                    attribute, enteredText);
                            }
                        }
                        break;
                    case AttributeControlType.MultilineTextbox:
                        {
                            var txtAttribute = form[controlId];
                            if (!String.IsNullOrEmpty(txtAttribute))
                            {
                                string enteredText = txtAttribute.Trim();
                                selectedAttributes = _productAttributeParser.AddProductAttribute(selectedAttributes,
                                    attribute, enteredText);
                            }
                        }
                        break;
                    case AttributeControlType.Datepicker:
                        {
                            var day = form[controlId + "_day"];
                            var month = form[controlId + "_month"];
                            var year = form[controlId + "_year"];
                            DateTime? selectedDate = null;
                            try
                            {
                                selectedDate = new DateTime(Int32.Parse(year), Int32.Parse(month), Int32.Parse(day));
                            }
                            catch { }
                            if (selectedDate.HasValue)
                            {
                                selectedAttributes = _productAttributeParser.AddProductAttribute(selectedAttributes,
                                    attribute, selectedDate.Value.ToString("D"));
                            }
                        }
                        break;
                    case AttributeControlType.FileUpload:
                        {
                            var httpPostedFile = this.Request.Files[controlId];
                            if ((httpPostedFile != null) && (!String.IsNullOrEmpty(httpPostedFile.FileName)))
                            {
                                int fileMaxSize = _catalogSettings.FileUploadMaximumSizeBytes;
                                if (httpPostedFile.ContentLength > fileMaxSize)
                                {
                                    warnings.Add(string.Format(_localizationService.GetResource("ShoppingCart.MaximumUploadedFileSize"), (int)(fileMaxSize / 1024)));
                                }
                                else
                                {
                                    //save an uploaded file
                                    var download = new Download()
                                    {
                                        DownloadGuid = Guid.NewGuid(),
                                        UseDownloadUrl = false,
                                        DownloadUrl = "",
                                        DownloadBinary = httpPostedFile.GetDownloadBits(),
                                        ContentType = httpPostedFile.ContentType,
                                        Filename = System.IO.Path.GetFileNameWithoutExtension(httpPostedFile.FileName),
                                        Extension = System.IO.Path.GetExtension(httpPostedFile.FileName),
                                        IsNew = true
                                    };
                                    _downloadService.InsertDownload(download);
                                    //save attribute
                                    selectedAttributes = _productAttributeParser.AddProductAttribute(selectedAttributes,
                                        attribute, download.DownloadGuid.ToString());
                                }
                            }
                        }
                        break;
                    default:
                        break;
                }
            }
            attributes = selectedAttributes;

            #endregion

            #region Gift cards

            string recipientName = "";
            string recipientEmail = "";
            string senderName = "";
            string senderEmail = "";
            string giftCardMessage = "";
            if (productVariant.IsGiftCard)
            {
                foreach (string formKey in form.AllKeys)
                {
                    if (formKey.Equals("giftcard.RecipientName", StringComparison.InvariantCultureIgnoreCase))
                    {
                        recipientName = form[formKey];
                        continue;
                    }
                    if (formKey.Equals("giftcard.RecipientEmail", StringComparison.InvariantCultureIgnoreCase))
                    {
                        recipientEmail = form[formKey];
                        continue;
                    }
                    if (formKey.Equals("giftcard.SenderName", StringComparison.InvariantCultureIgnoreCase))
                    {
                        senderName = form[formKey];
                        continue;
                    }
                    if (formKey.Equals("giftcard.SenderEmail", StringComparison.InvariantCultureIgnoreCase))
                    {
                        senderEmail = form[formKey];
                        continue;
                    }
                    if (formKey.Equals("giftcard.Message", StringComparison.InvariantCultureIgnoreCase))
                    {
                        giftCardMessage = form[formKey];
                        continue;
                    }
                }

                attributes = _productAttributeParser.AddGiftCardAttribute(attributes,
                    recipientName, recipientEmail, senderName, senderEmail, giftCardMessage);
            }

            #endregion

            //warnings
            warnings.AddRange(_shoppingCartService.GetShoppingCartItemAttributeWarnings(ShoppingCartType.ShoppingCart, productVariant, attributes));
            warnings.AddRange(_shoppingCartService.GetShoppingCartItemGiftCardWarnings(ShoppingCartType.ShoppingCart, productVariant, attributes));
            if (warnings.Count == 0)
            {
                //no errors

                //attributes
                string attributeDescription = _productAttributeFormatter.FormatAttributes(productVariant, attributes, order.Customer);

                //save item
                var opv = new OrderProductVariant()
                {
                    OrderProductVariantGuid = Guid.NewGuid(),
                    Order = order,
                    ProductVariantId = productVariant.Id,
                    UnitPriceInclTax = unitPriceInclTax,
                    UnitPriceExclTax = unitPriceExclTax,
                    PriceInclTax = priceInclTax,
                    PriceExclTax = priceExclTax,
                    AttributeDescription = attributeDescription,
                    AttributesXml = attributes,
                    Quantity = quantity,
                    DiscountAmountInclTax = decimal.Zero,
                    DiscountAmountExclTax = decimal.Zero,
                    DownloadCount = 0,
                    IsDownloadActivated = false,
                    LicenseDownloadId = 0
                };
                order.OrderProductVariants.Add(opv);
                _orderService.UpdateOrder(order);

                //gift cards
                if (productVariant.IsGiftCard)
                {
                    for (int i = 0; i < opv.Quantity; i++)
                    {
                        var gc = new GiftCard()
                        {
                            GiftCardType = productVariant.GiftCardType,
                            PurchasedWithOrderProductVariant = opv,
                            Amount = unitPriceExclTax,
                            IsGiftCardActivated = false,
                            GiftCardCouponCode = _giftCardService.GenerateGiftCardCode(),
                            RecipientName = recipientName,
                            RecipientEmail = recipientEmail,
                            SenderName = senderName,
                            SenderEmail = senderEmail,
                            Message = giftCardMessage,
                            IsRecipientNotified = false,
                            CreatedOnUtc = DateTime.UtcNow
                        };
                        _giftCardService.InsertGiftCard(gc);
                    }
                }

                //redirect to order details page
                return RedirectToAction("Edit", "Order", new { id = order.Id });
            }
            else
            {
                //errors
                var model = PrepareAddProductToOrderModel(order.Id, productVariant.Id);
                model.Warnings.AddRange(warnings);
                return View(model);
            }
        }

        #endregion

        #region Logistics service
        [HttpPost]
        public ActionResult CalculateNewCharge(OrderModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();
            decimal subtTotalExclTax = 0;
            decimal length = 0;
            decimal width = 0;
            decimal height = 0;
            decimal weight = 0;
            int pkgId = 0;
            bool updateDimensionAndWeightInfo = false;
            bool isAddingServiceFee = false;
            //calculate different price
            var shipmentPackageModel = model.ShipmentPackageModel;
            int packageId = shipmentPackageModel.PackageId ?? 0;

            try
            {
                var pkg = _logisticsService.GetPackageById(packageId);
                if (pkg != null && pkg.Status != ShippingStatus.Cancelled && pkg.Status != ShippingStatus.CancelledNotRefund && pkg.Status != ShippingStatus.CancelledWithRefund)
                {
                    if (pkg.Shipment.OrderId == model.Id)
                    {
                        var previousChargeSubTotal = CalculateChargeTotal(null, pkg).SubTotalExclTax;
                        var chargeSubTotal = CalculateChargeTotal(shipmentPackageModel, pkg).SubTotalExclTax;
                        subtTotalExclTax = chargeSubTotal - previousChargeSubTotal;
                        if (subtTotalExclTax < 0)
                        {
                            subtTotalExclTax = 0;
                            ModelState.AddModelError("", _localizationService.GetResource("Admin.Order.CalculateNewCharge.Package.NotOverSize"));
                        }
                        else
                        {
                            if (shipmentPackageModel.IsAddingServiceFee)
                                subtTotalExclTax = subtTotalExclTax + _logisticsInformationSettings.SpecialServiceCharge;
                            pkgId = packageId;
                            if (shipmentPackageModel.UpdateDimensionAndWeightInfo)
                            {
                                length = shipmentPackageModel.Length;
                                width = shipmentPackageModel.Width;
                                height = shipmentPackageModel.Height;
                                weight = shipmentPackageModel.Weight;
                                updateDimensionAndWeightInfo = true;
                                isAddingServiceFee = shipmentPackageModel.IsAddingServiceFee;
                            }
                        }
                    }
                    else
                    {
                        ModelState.AddModelError("", _localizationService.GetResource("Admin.Order.Package.NotInOrder"));
                    }
                }
                else
                {
                    ModelState.AddModelError("", _localizationService.GetResource("Admin.Order.Package.NotNullNotCancel"));
                }
            }
            catch (Exception ex)
            {
                ModelState.AddModelError("", ex.Message);
            }
            if (!ModelState.IsValid)
            {
                return RedirectToAction("Edit", new { id = model.Id });
            }

            return RedirectToAction("AddLogisticsService", new { orderId = model.Id, subTotalExclTax = subtTotalExclTax, length = length, width = width, height = height, weight = weight, pkgId = pkgId, updateDimensionAndWeightInfo = updateDimensionAndWeightInfo, isAddingServiceFee = isAddingServiceFee });
        }

        public ActionResult AddLogisticsService(int orderId, decimal subTotalExclTax, decimal length, decimal width, decimal height, decimal weight, int pkgId, bool updateDimensionAndWeightInfo, bool isAddingServiceFee)
        {
            var productDetailsModel = PrepareAddProductToOrderModel(orderId, _logisticsInformationSettings.SpecialServiceProductVarianId);
            productDetailsModel.SubTotalExclTax = subTotalExclTax;
            productDetailsModel.ShipmentPackageModel = new ShipmentPackageModel();
            productDetailsModel.ShipmentPackageModel.UpdateDimensionAndWeightInfo = false;
            productDetailsModel.ShipmentPackageModel.PackageId = pkgId;
            if (updateDimensionAndWeightInfo)
            {
                productDetailsModel.ShipmentPackageModel.Length = length;
                productDetailsModel.ShipmentPackageModel.Width = width;
                productDetailsModel.ShipmentPackageModel.Height = height;
                productDetailsModel.ShipmentPackageModel.Weight = weight;
                productDetailsModel.ShipmentPackageModel.UpdateDimensionAndWeightInfo = updateDimensionAndWeightInfo;
                productDetailsModel.ShipmentPackageModel.IsAddingServiceFee = isAddingServiceFee;
            }

            return View(productDetailsModel);
        }

        [HttpPost]
        public ActionResult AddLogisticsService(int orderId, int productVariantId, int packageId, bool updateDimensionAndWeightInfo, bool isAddingServiceFee, FormCollection form)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(orderId);
            if (order.OrderKind != OrderKind.Express)
            {
                return AccessDeniedView();
            }
            //warnings
            var warnings = new List<string>();
            var productVariant = _productService.GetProductVariantById(productVariantId);

            try
            {
                //basic properties
                var quantity = 1;
                var priceInclTax = decimal.Zero;
                decimal.TryParse(form["SubTotalInclTax"], out priceInclTax);
                var priceExclTax = decimal.Zero;
                decimal.TryParse(form["SubTotalExclTax"], out priceExclTax);

                // See if front end requests a new order be generated for the sole purpose of
                // containing the logistics service.
                bool createNewOrder = false;
                Boolean.TryParse(form["CreateNewOrder"], out createNewOrder);

                if (createNewOrder)
                {
                    #region Create Order

                    var parentOrder = order;

                    var languageId = _workContext.WorkingLanguage.Id;
                    var customer = parentOrder.Customer;

                    //TODO: Too much hard coded here
                    order = new Order()
                    {
                        OrderGuid = Guid.NewGuid(),
                        Customer = customer,
                        CustomerLanguageId = languageId,
                        CustomerTaxDisplayType = Nop.Core.Domain.Tax.TaxDisplayType.IncludingTax,
                        CustomerIp = _webHelper.GetCurrentIpAddress(),
                        OrderSubtotalInclTax = priceInclTax,
                        OrderSubtotalExclTax = priceExclTax,
                        OrderSubTotalDiscountInclTax = decimal.Zero,
                        OrderSubTotalDiscountExclTax = decimal.Zero,
                        OrderShippingInclTax = decimal.Zero,
                        OrderShippingExclTax = decimal.Zero,
                        PaymentMethodAdditionalFeeInclTax = decimal.Zero,
                        PaymentMethodAdditionalFeeExclTax = decimal.Zero,
                        TaxRates = _logisticsInformationSettings.TaxRate.ToString(),
                        OrderTax = priceInclTax - priceExclTax,
                        OrderTotal = priceInclTax,
                        RefundedAmount = decimal.Zero,
                        OrderDiscount = decimal.Zero,
                        CheckoutAttributeDescription = string.Empty,
                        CheckoutAttributesXml = string.Empty,
                        CustomerCurrencyCode = "CAD",
                        CurrencyRate = 1M,
                        AffiliateId = null,
                        OrderStatus = OrderStatus.Processing,
                        AllowStoringCreditCardNumber = false,
                        CardType = string.Empty,
                        CardName = string.Empty,
                        CardNumber = string.Empty,
                        MaskedCreditCardNumber = string.Empty,
                        CardCvv2 = string.Empty,
                        CardExpirationMonth = string.Empty,
                        CardExpirationYear = string.Empty,
                        PaymentMethodSystemName = string.Empty,
                        AuthorizationTransactionId = string.Empty,
                        AuthorizationTransactionCode = string.Empty,
                        AuthorizationTransactionResult = string.Empty,
                        CaptureTransactionId = string.Empty,
                        CaptureTransactionResult = string.Empty,
                        SubscriptionTransactionId = string.Empty,
                        PurchaseOrderNumber = string.Empty,
                        PaymentStatus = Nop.Core.Domain.Payments.PaymentStatus.Pending,
                        PaidDateUtc = null,
                        BillingAddress = customer.BillingAddress,
                        ShippingAddress = customer.ShippingAddress,
                        ShippingStatus = ShippingStatus.ShippingNotRequired,
                        ShippingMethod = string.Empty,
                        ShippingRateComputationMethodSystemName = string.Empty,
                        VatNumber = string.Empty,
                        CreatedOnUtc = DateTime.UtcNow,
                        OrderKind = OrderKind.Express,
                        BoundOrderId = parentOrder.Id,          // Bound this new order to the original orderId
                        DistrictId = parentOrder.DistrictId
                    };
                    _orderService.InsertOrder(order);

                    #endregion
                }

                //save order item
                //attributes
                string attributes = "";

                #region Product attributes
                string selectedAttributes = string.Empty;
                var productVariantAttributes = _productAttributeService.GetProductVariantAttributesByProductVariantId(productVariant.Id);
                foreach (var attribute in productVariantAttributes)
                {
                    string controlId = string.Format("product_attribute_{0}_{1}", attribute.ProductAttributeId, attribute.Id);
                    switch (attribute.AttributeControlType)
                    {
                        case AttributeControlType.TextBox:
                            {
                                var txtAttribute = form[controlId];
                                if (!String.IsNullOrEmpty(txtAttribute))
                                {
                                    string enteredText = txtAttribute.Trim();
                                    selectedAttributes = _productAttributeParser.AddProductAttribute(selectedAttributes,
                                        attribute, enteredText);
                                }
                            }
                            break;
                        case AttributeControlType.MultilineTextbox:
                            {
                                var txtAttribute = form[controlId];
                                if (!String.IsNullOrEmpty(txtAttribute))
                                {
                                    string enteredText = txtAttribute.Trim();
                                    selectedAttributes = _productAttributeParser.AddProductAttribute(selectedAttributes,
                                        attribute, enteredText);
                                }
                            }
                            break;
                        default:
                            break;
                    }
                }
                attributes = selectedAttributes;

                #endregion

                //warnings
                warnings.AddRange(_shoppingCartService.GetShoppingCartItemAttributeWarnings(ShoppingCartType.ShoppingCart, productVariant, attributes));
                if (warnings.Count == 0)
                {
                    //no errors
                    var taxSurchargeList = new List<Surcharge>();
                    if (_logisticsInformationSettings.TaxCategoryId == 0)
                    {
                        throw new NopException("Tax category must be set first.");
                    }

                    var taxRates = _taxService.GetTaxRate(_logisticsInformationSettings.TaxCategoryId, order.Customer);
                    if (taxRates == null || taxRates.Count == 0)
                    {
                        throw new NopException("Tax rates must be set for tax category {0}.", _logisticsInformationSettings.TaxCategoryId);
                    }

                    //attributes
                    string attributeDescription = _productAttributeFormatter.FormatAttributes(productVariant, attributes, order.Customer);

                    //save item
                    var opv = new OrderProductVariant()
                    {
                        OrderProductVariantGuid = Guid.NewGuid(),
                        Order = order,
                        ProductVariantId = productVariant.Id,
                        UnitPriceInclTax = priceInclTax,
                        UnitPriceExclTax = priceExclTax,
                        PriceInclTax = priceInclTax,
                        PriceExclTax = priceExclTax,
                        AttributeDescription = attributeDescription,
                        AttributesXml = attributes,
                        Quantity = quantity,
                        DiscountAmountInclTax = decimal.Zero,
                        DiscountAmountExclTax = decimal.Zero,
                        DownloadCount = 0,
                        IsDownloadActivated = false,
                        LicenseDownloadId = 0
                    };
                    order.OrderProductVariants.Add(opv);
                    //add surcharge
                    opv.PriceExclTax = opv.PriceExclTax * (1 + _logisticsInformationSettings.FuelSurchargeRate);
                    taxRates.ForEach(t => t.Value = opv.PriceExclTax * t.PercentageOrFee / 100);
                    taxSurchargeList.AddRange(taxRates.ToSurcharges(null, opv));
                    _taxService.AddSurchargesToOrder(order, taxSurchargeList);

                    _orderService.UpdateOrder(order);

                    //update package note
                    if (updateDimensionAndWeightInfo)
                    {
                        var package = _logisticsService.GetPackageById(packageId);
                        if (package != null)
                        {
                            var length = decimal.Zero;
                            decimal.TryParse(form["ShipmentPackageModel.Length"], out length);
                            var height = decimal.Zero;
                            decimal.TryParse(form["ShipmentPackageModel.Height"], out height);
                            var width = decimal.Zero;
                            decimal.TryParse(form["ShipmentPackageModel.Width"], out width);
                            var weight = decimal.Zero;
                            decimal.TryParse(form["ShipmentPackageModel.Weight"], out weight);

                            string packageSizeStr = "";
                            var shipment = package.Shipment;
                            var note = String.Format("The package tracking number is {0}.", shipment.TrackingNumber);
                            int spotshubPackageSize = shipment.SpotshubPackageSize;
                            bool hasPackageSize = true;
                            switch (spotshubPackageSize)
                            {
                                case 0: hasPackageSize = false;
                                    break;
                                case 1: packageSizeStr = _localizationService.GetResource("Logistics.Order.Package.Size.Letter");
                                    break;
                                case 2: packageSizeStr = _localizationService.GetResource("Logistics.Order.Package.CompactSize");
                                    break;
                                case 3: packageSizeStr = _localizationService.GetResource("Logistics.Order.Package.FullSize");
                                    break;
                            }
                            var baseWeight = _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId);
                            var baseWeightIn = baseWeight != null ? baseWeight.Name : "";
                            var baseDimension = _measureService.GetMeasureDimensionById(_measureSettings.BaseDimensionId);
                            var baseDimensionIn = baseDimension != null ? baseDimension.Name : "";
                            string pkgNoteContent = !hasPackageSize ? string.Format("Previous package measure: {1:###.##}{0} x {2:###.##}{0} x {3:###.##}{0} {4:##.##}{5}", baseDimensionIn,
                                                        MathEx.Round(shipment.Length.Value, 2), MathEx.Round(shipment.Width.Value, 2), MathEx.Round(shipment.Height.Value, 2),
                                                        MathEx.Round(shipment.TotalWeight.Value, 2), baseWeightIn) : String.Format("Previous package measurement is {0}.", packageSizeStr);
                            //add dimensiona and weight info to attributes 
                            decimal previousShipmentTotal = 0;
                            if (order.PaymentStatus == PaymentStatus.Paid)
                            {
                                //only show for paper use customer
                                opv.AttributeDescription = String.Format("{0} {1} {2}", note, opv.AttributeDescription, pkgNoteContent);
                                _orderService.UpdateOrder(order);
                                previousShipmentTotal = CalculateChargeTotal(null, package).Total;
                            }
                            shipment.Length = length;
                            shipment.Width = width;
                            shipment.Height = height;
                            shipment.TotalWeight = weight;
                            shipment.SpotshubPackageSize = 0;
                            _logisticsService.CreatePackageNote(package.Id, PackageNoteType.SystemEvent, String.Format("{0} {1}", note, pkgNoteContent), _workContext.CurrentCustomer.Id, false, null, null);
                            _logisticsService.UpdatePackage(package, false);

                            decimal shipmentTotal = 0;
                            if (order.PaymentStatus == PaymentStatus.Paid)
                            {
                                //only show for paper use customer
                                shipmentTotal = CalculateChargeTotal(null, package).Total;
                                decimal difference = shipmentTotal - previousShipmentTotal;
                                if (difference > 0)
                                {
                                    string specialServiceChargeNote = "";
                                    decimal specialServiceCharge = _logisticsInformationSettings.SpecialServiceCharge;
                                    if (isAddingServiceFee)
                                    {
                                        shipmentTotal += specialServiceCharge;
                                        difference += specialServiceCharge;
                                        specialServiceChargeNote = String.Format(_localizationService.GetResource("Admin.Order.CalculateNewCharge.SpecialService"), _priceFormatter.FormatPrice(specialServiceCharge));
                                    }

                                    string pkgDifferenceNote = String.Format(_localizationService.GetResource("Admin.Order.CalculateNewCharge.PreviousePackageShipment"),
                                                _priceFormatter.FormatPrice(previousShipmentTotal), _priceFormatter.FormatPrice(shipmentTotal),
                                                _priceFormatter.FormatPrice(difference), specialServiceChargeNote);
                                    opv.AttributeDescription = String.Format("{0} {1}", opv.AttributeDescription, pkgDifferenceNote);
                                    _orderService.UpdateOrder(order);
                                }
                            }
                        }
                        else
                        {
                            warnings.Add(_localizationService.GetResource("Admin.Order.Package.IsNull"));
                        }
                    }

                    decimal fuelSurchargeRate = _logisticsInformationSettings.FuelSurchargeRate;
                    decimal taxRate = _logisticsInformationSettings.TaxRate;

                    if (priceExclTax > 0)
                    {
                        //Add note when change order amount
                        decimal fuelSurcharge = priceExclTax * fuelSurchargeRate;
                        decimal tax = (priceExclTax + fuelSurcharge) * taxRate;
                        decimal differenceOrderTotal = priceExclTax + fuelSurcharge + tax;
                        var orderNote = new OrderNote()
                        {
                            DisplayToCustomer = false,
                            Note = String.Format(_localizationService.GetResource("Admin.Order.CalculateNewCharge.OrderAmountAndDifference"),
                                        _workContext.CurrentCustomer.Email, packageId, _priceFormatter.FormatPrice(order.OrderSubtotalExclTax), _priceFormatter.FormatPrice(order.OrderShippingExclTax), _priceFormatter.FormatPrice(order.OrderTax),
                                        _priceFormatter.FormatPrice(order.OrderTotal), _priceFormatter.FormatPrice(differenceOrderTotal)),
                            CreatedOnUtc = DateTime.UtcNow,
                        };
                        order.OrderNotes.Add(orderNote);

                        order.OrderSubtotalExclTax += priceExclTax;
                        order.OrderShippingExclTax += fuelSurcharge;
                        order.OrderTax += tax;
                        order.OrderTotal = order.OrderSubtotalExclTax + order.OrderShippingExclTax + order.OrderTax;
                        _orderService.UpdateOrder(order);

                        //update invoice if included
                        if (order.InvoiceId.HasValue)
                        {
                            var invoice = _invoiceService.GetInvoiceById(order.InvoiceId.Value);
                            if (UpdateInvoiceAfterOrderChange(invoice, order.Id))
                                SuccessNotification(String.Format(_localizationService.GetResource("Admin.Invoice.Invoiceupdate"), _workContext.CurrentCustomer.Email));
                        }
                    }

                    //redirect to order details page
                    if (warnings.Count == 0)
                    {
                        return RedirectToAction("Edit", "Order", new { id = order.Id });
                    }
                }
            }
            catch (Exception ex)
            {
                warnings.Add(ex.Message);
            }
            //errors
            var model = PrepareAddProductToOrderModel(order.Id, productVariant.Id);
            model.Warnings.AddRange(warnings);
            return View(model);
        }

        public ActionResult CancelPackage(int id, int orderId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var model = new OrderModel.OrderProductVariantModel();
            model.Id = id;
            model.OrderId = orderId;
            model.IsRefund = false;

            return View(model);
        }

        [HttpPost]
        public ActionResult CancelPackage(OrderModel.OrderProductVariantModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var customer = _workContext.CurrentCustomer;

            var order = _orderService.GetOrderById(model.OrderId);
            if (order == null || order.OrderKind != OrderKind.Express)
                //No order found with the specified id
                return RedirectToAction("List");

            try
            {
                //get order product variant identifier
                int opvId = model.Id;
                bool refund = model.IsRefund ?? false;

                if (String.IsNullOrEmpty(model.CancelReason))
                {
                    ErrorNotification(_localizationService.GetResource("Admin.Order.CancelPackage.CancelReason.Notnull"));
                }
                else
                {
                    var orderProductVariant = order.OrderProductVariants.Where(x => x.Id == opvId).FirstOrDefault();
                    if (orderProductVariant == null)
                        throw new NopException("No order product variant found with the specified id: {0}", opvId);

                    var shipment = order.Shipments.Where(s =>
                        s.ShipmentOrderProductVariants.Single().OrderProductVariantId == opvId).FirstOrDefault();
                    if (shipment != null)
                    {
                        var pkg = _logisticsService.GetPackageByShipmentId(shipment.Id);

                        // TBD: Latest discussion is to re-route the package back to destination when a package is cancelled and it is already on the road.
                        //      So, we cannot (always) just mark package status as Cancelled. It may very well be changed to simply ReturningToWarehouse.
                        //      But, if package is not yet picked up, then I guess, yes, we can have Cancelled status on the package (to prevent pickup or even planning).
                        if (pkg != null && pkg.Status != ShippingStatus.Cancelled && pkg.Status != ShippingStatus.CancelledWithRefund)
                        {
                            if (pkg.Status == ShippingStatus.CancelledNotRefund && !refund)
                            {
                                ErrorNotification(String.Format(_localizationService.GetResource("Admin.Order.CancelPackage.NotCancelAgain"), pkg.Id));
                                return View(model);
                            }

                            if (order.PaymentStatus == PaymentStatus.Pending)
                            {
                                pkg.Status = ShippingStatus.Cancelled;
                            }
                            else
                            {
                                pkg.Status = ShippingStatus.CancelledNotRefund;
                            }

                            // TBD: Currently cannot log a message, as no matching PlanNoteStatus for that. (see above TBD).
                            _logisticsService.UpdateLogisticsPackage(pkg, PlanNoteStatus.UpdatePackage, customer.Id, createLog: false);
                            string cancelReason = String.Format(_localizationService.GetResource("Admin.Order.CancelPackage.CancelReason"), model.CancelReason);

                            if (refund)
                            {
                                //Add note when change order amount
                                var orderNote = new OrderNote()
                                {
                                    DisplayToCustomer = false,
                                    Note = String.Format(_localizationService.GetResource("Admin.Order.CancelPackage.OrderAmountChange"),
                                                _workContext.CurrentCustomer.Email, _priceFormatter.FormatPrice(order.OrderSubtotalExclTax), _priceFormatter.FormatPrice(order.OrderShippingExclTax), _priceFormatter.FormatPrice(order.OrderTax),
                                                _priceFormatter.FormatPrice(order.OrderTotal)),
                                    CreatedOnUtc = DateTime.UtcNow,
                                };
                                order.OrderNotes.Add(orderNote);

                                var price = orderProductVariant.PriceExclTax;
                                orderProductVariant.PriceExclTax = 0M;
                                order.OrderSubtotalExclTax -= price;
                                if (order.OrderSubtotalExclTax < 0)
                                    order.OrderSubtotalExclTax = 0;
                                order.OrderShippingExclTax -= price * _logisticsInformationSettings.FuelSurchargeRate;
                                if (order.OrderShippingExclTax < 0)
                                    order.OrderShippingExclTax = 0;
                                order.OrderTax = (order.OrderShippingExclTax + order.OrderSubtotalExclTax) * _logisticsInformationSettings.TaxRate;
                                order.OrderTotal = order.OrderTax + order.OrderShippingExclTax + order.OrderSubtotalExclTax;
                                _taxService.DeleteOPVRelatedSurcharge(opvId);

                                _logisticsService.CreatePackageNote(pkg.Id, PackageNoteType.SystemEvent, cancelReason, customer.Id, false, ShippingStatus.CancelledWithRefund, pkg.PickUpLocation.Address);
                                _orderService.UpdateOrder(order);
                                SuccessNotification(_localizationService.GetResource("Admin.Orders.OrderAmount.Update"));

                                if (pkg.Status == ShippingStatus.CancelledNotRefund)
                                {
                                    pkg.Status = ShippingStatus.CancelledWithRefund;
                                    SuccessNotification(String.Format(_localizationService.GetResource("Admin.Order.CancelPackage.Successfully"), customer.Email));
                                }

                                //update shipment
                                string comment = shipment.Comment;
                                shipment.Comment = String.Format("{0} {1}", comment, cancelReason);
                                _shipmentService.UpdateShipment(shipment);
                                //update invoice if included
                                if (order.InvoiceId.HasValue)
                                {
                                    var invoice = _invoiceService.GetInvoiceById(order.InvoiceId.Value);
                                    if (UpdateInvoiceAfterOrderChange(invoice, order.Id))
                                        SuccessNotification(String.Format(_localizationService.GetResource("Admin.Invoice.Invoiceupdate"), customer.Email));
                                }
                            }
                            else
                            {
                                _logisticsService.CreatePackageNote(pkg.Id, PackageNoteType.SystemEvent, model.CancelReason, customer.Id, false, null, null);
                                //Add note when have cancel reason
                                var orderNote = new OrderNote()
                                {
                                    DisplayToCustomer = false,
                                    Note = String.Format(_localizationService.GetResource("Admin.Order.Package.CancelReason"), pkg.Id, cancelReason),
                                    CreatedOnUtc = DateTime.UtcNow,
                                };
                                order.OrderNotes.Add(orderNote);
                            }

                            if (order.Shipments.All(x => _logisticsService.GetPackageByShipmentId(x.Id).Status == ShippingStatus.Cancelled))
                            {
                                order.ShippingStatus = ShippingStatus.Cancelled;
                            }
                            else if (order.Shipments.Any(x => _logisticsService.GetPackageByShipmentId(x.Id).Status == ShippingStatus.CancelledNotRefund))
                            {
                                order.ShippingStatus = ShippingStatus.CancelledNotRefund;
                            }
                            else if (order.Shipments.Any(x => _logisticsService.GetPackageByShipmentId(x.Id).Status == ShippingStatus.CancelledWithRefund))
                            {
                                order.ShippingStatus = ShippingStatus.CancelledWithRefund;
                            }

                            _orderService.UpdateOrder(order);

                            SuccessNotification(_localizationService.GetResource("Admin.Orders.Shipments.PackageCancelled"));
                            _customerActivityService.InsertActivity("CancelOrder", _localizationService.GetResource("ActivityLog.CancelPackage"), pkg.Id);
                            return View(model);
                        }
                        else
                        {
                            ErrorNotification(_localizationService.GetResource("Admin.Order.CancelPackage.NotNullNotCancel"));
                        }
                    }
                }

                // if sth failed
                model.Id = order.Id;
                model.OrderId = opvId;
                model.IsRefund = refund;
                return View(model);
            }
            catch (Exception exc)
            {
                //error
                ErrorNotification(exc, false);
                return RedirectToAction("Edit", new { id = model.OrderId });
            }
        }

        #endregion

        #endregion

        #region Addresses

        public ActionResult AddressEdit(int addressId, int orderId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(orderId);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            var address = _addressService.GetAddressById(addressId);
            if (address == null)
                throw new ArgumentException("No address found with the specified id", "addressId");

            var model = new OrderAddressModel();
            model.OrderId = orderId;
            model.Address = address.ToModel();
            model.Address.FirstNameEnabled = true;
            model.Address.FirstNameRequired = true;
            model.Address.LastNameEnabled = true;
            model.Address.LastNameRequired = true;
            model.Address.EmailEnabled = true;
            model.Address.EmailRequired = true;
            model.Address.CompanyEnabled = _addressSettings.CompanyEnabled;
            model.Address.CompanyRequired = _addressSettings.CompanyRequired;
            model.Address.CountryEnabled = _addressSettings.CountryEnabled;
            model.Address.StateProvinceEnabled = _addressSettings.StateProvinceEnabled;
            model.Address.CityEnabled = _addressSettings.CityEnabled;
            model.Address.CityRequired = _addressSettings.CityRequired;
            model.Address.StreetAddressEnabled = _addressSettings.StreetAddressEnabled;
            model.Address.StreetAddressRequired = _addressSettings.StreetAddressRequired;
            model.Address.StreetAddress2Enabled = _addressSettings.StreetAddress2Enabled;
            model.Address.StreetAddress2Required = _addressSettings.StreetAddress2Required;
            model.Address.ZipPostalCodeEnabled = _addressSettings.ZipPostalCodeEnabled;
            model.Address.ZipPostalCodeRequired = _addressSettings.ZipPostalCodeRequired;
            model.Address.CellPhoneEnabled = _addressSettings.CellPhoneEnabled;
            model.Address.CellPhoneRequired = _addressSettings.CellPhoneRequired;
            model.Address.PhoneEnabled = _addressSettings.PhoneEnabled;
            model.Address.PhoneRequired = _addressSettings.PhoneRequired;
            model.Address.FaxEnabled = _addressSettings.FaxEnabled;
            model.Address.FaxRequired = _addressSettings.FaxRequired;

            //countries
            model.Address.AvailableCountries.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.SelectCountry"), Value = "0" });
            foreach (var c in _countryService.GetAllCountries(true))
                model.Address.AvailableCountries.Add(new SelectListItem() { Text = c.Name, Value = c.Id.ToString(), Selected = (c.Id == address.CountryId) });
            //states
            var states = address.Country != null ? _stateProvinceService.GetStateProvincesByCountryId(address.Country.Id, true).ToList() : new List<StateProvince>();
            if (states.Count > 0)
            {
                foreach (var s in states)
                    model.Address.AvailableStates.Add(new SelectListItem() { Text = s.Name, Value = s.Id.ToString(), Selected = (s.Id == address.StateProvinceId) });
            }
            else
                model.Address.AvailableStates.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.OtherNonUS"), Value = "0" });

            return View(model);
        }

        [HttpPost]
        public ActionResult AddressEdit(OrderAddressModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(model.OrderId);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            var address = _addressService.GetAddressById(model.Address.Id);
            if (address == null)
                throw new ArgumentException("No address found with the specified id");

            if (ModelState.IsValid)
            {
                address = model.Address.ToEntity(address);
                _addressService.UpdateAddress(address);

                return RedirectToAction("AddressEdit", new { addressId = model.Address.Id, orderId = model.OrderId });
            }

            //If we got this far, something failed, redisplay form
            model.OrderId = order.Id;
            model.Address = address.ToModel();
            model.Address.FirstNameEnabled = true;
            model.Address.FirstNameRequired = true;
            model.Address.LastNameEnabled = true;
            model.Address.LastNameRequired = true;
            model.Address.EmailEnabled = true;
            model.Address.EmailRequired = true;
            model.Address.CompanyEnabled = _addressSettings.CompanyEnabled;
            model.Address.CompanyRequired = _addressSettings.CompanyRequired;
            model.Address.CountryEnabled = _addressSettings.CountryEnabled;
            model.Address.StateProvinceEnabled = _addressSettings.StateProvinceEnabled;
            model.Address.CityEnabled = _addressSettings.CityEnabled;
            model.Address.CityRequired = _addressSettings.CityRequired;
            model.Address.StreetAddressEnabled = _addressSettings.StreetAddressEnabled;
            model.Address.StreetAddressRequired = _addressSettings.StreetAddressRequired;
            model.Address.StreetAddress2Enabled = _addressSettings.StreetAddress2Enabled;
            model.Address.StreetAddress2Required = _addressSettings.StreetAddress2Required;
            model.Address.ZipPostalCodeEnabled = _addressSettings.ZipPostalCodeEnabled;
            model.Address.ZipPostalCodeRequired = _addressSettings.ZipPostalCodeRequired;
            model.Address.CellPhoneEnabled = _addressSettings.CellPhoneEnabled;
            model.Address.CellPhoneRequired = _addressSettings.CellPhoneRequired;
            model.Address.PhoneEnabled = _addressSettings.PhoneEnabled;
            model.Address.PhoneRequired = _addressSettings.PhoneRequired;
            model.Address.FaxEnabled = _addressSettings.FaxEnabled;
            model.Address.FaxRequired = _addressSettings.FaxRequired;
            //countries
            model.Address.AvailableCountries.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.SelectCountry"), Value = "0" });
            foreach (var c in _countryService.GetAllCountries(true))
                model.Address.AvailableCountries.Add(new SelectListItem() { Text = c.Name, Value = c.Id.ToString(), Selected = (c.Id == address.CountryId) });
            //states
            var states = address.Country != null ? _stateProvinceService.GetStateProvincesByCountryId(address.Country.Id, true).ToList() : new List<StateProvince>();
            if (states.Count > 0)
            {
                foreach (var s in states)
                    model.Address.AvailableStates.Add(new SelectListItem() { Text = s.Name, Value = s.Id.ToString(), Selected = (s.Id == address.StateProvinceId) });
            }
            else
                model.Address.AvailableStates.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.OtherNonUS"), Value = "0" });

            return View(model);
        }

        #endregion

        #region Shipments


        public ActionResult ShipmentList()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var model = new ShipmentListModel();
            model.DisplayPdfPackagingSlip = _pdfSettings.Enabled;
            return View(model);
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult ShipmentListSelect(GridCommand command, ShipmentListModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            DateTime? startDateValue = (model.StartDate == null) ? null
                            : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.StartDate.Value, _dateTimeHelper.CurrentTimeZone);

            DateTime? endDateValue = (model.EndDate == null) ? null
                            : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.EndDate.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1);

            //load shipments
            var shipments = _shipmentService.GetAllShipments(startDateValue, endDateValue,
                command.Page - 1, command.PageSize);
            var gridModel = new GridModel<ShipmentModel>
            {
                Data = shipments.Select(shipment => PrepareShipmentModel(shipment, false)),
                Total = shipments.TotalCount
            };
            return new JsonResult
            {
                Data = gridModel
            };
        }

        [HttpPost, GridAction(EnableCustomBinding = true)]
        public ActionResult ShipmentsSelect(int orderId, GridCommand command)
        {
            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageOrders);
            if (curManagedStoreId != Store.AllStoreReservedId && _orderService.GetStoreIdByOrderId(orderId) != curManagedStoreId)
            {
                return AccessDeniedView();
            }

            var order = _orderService.GetOrderById(orderId);
            if (order == null)
                throw new ArgumentException("No order found with the specified id");

            //shipments
            var shipmentModels = new List<ShipmentModel>();
            var shipments = order.Shipments.OrderBy(s => s.CreatedOnUtc).ToList();
            foreach (var shipment in shipments)
                shipmentModels.Add(PrepareShipmentModel(shipment, false));

            var model = new GridModel<ShipmentModel>
            {
                Data = shipmentModels,
                Total = shipmentModels.Count
            };

            return new JsonResult
            {
                Data = model
            };
        }

        public ActionResult AddShipment(int orderId)
        {
            var order = _orderService.GetOrderById(orderId);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            if (order.OrderStatusId == (int)OrderStatus.Cancelled)
            {
                ErrorNotification("this order is cancelled");
                return RedirectToAction("Edit", new { id = orderId });
            }

            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageOrders);
            if (curManagedStoreId != Store.AllStoreReservedId && _orderService.GetStoreIdByOrderId(orderId) != curManagedStoreId)
            {
                return AccessDeniedView();
            }

            var customer = _workContext.CurrentCustomer;
            if (customer.ShippingAddress == null)
            {
                ErrorNotification(_localizationService.GetResource("Admin.Orders.Shipments.NoShippingAddress"));
                return RedirectToAction("Edit", new { id = orderId });
            }

            _httpContext.Session["packages"] = null;

            var model = new ShipmentModel()
            {
                OrderId = order.Id,
            };

            PrepareAddShipmentModel(model, order);

            return View(model);
        }

        [HttpPost, ActionName("AddShipment")]
        [FormValueRequired("btnAdd")]
        public ActionResult AddPackage(int orderId, ShipmentModel model)
        {
            var order = _orderService.GetOrderById(orderId);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageOrders);
            if (curManagedStoreId != Store.AllStoreReservedId && _orderService.GetStoreIdByOrderId(orderId) != curManagedStoreId)
            {
                return AccessDeniedView();
            }
            /*
            if (model.CurrentPackage.Products.All(x => x.QuantityToAdd == 0))
            {
                ErrorNotification(_localizationService.GetResource("Admin.Orders.Shipments.NoProductsSelected"));
            }
            else if (ModelState.IsValid)
            {
                model.CurrentPackage.Products.RemoveAll(x => x.QuantityToAdd == 0);

                var packages = _httpContext.Session["packages"] as List<ShipmentPackageModel>;

                if (packages != null)
                {
                    model.Packages = packages;
                }
                else
                {
                    model.Packages = new List<ShipmentPackageModel>();
                }
                
                if (model.CurrentPackage != null)
                {
                    model.Packages.Add(model.CurrentPackage);
                    model.CurrentPackage = new ShipmentPackageModel() { IsStandard = true };
                }
                
                _httpContext.Session["packages"] = model.Packages;
                ModelState.Clear();
            }
            */
            PrepareAddShipmentModel(model, order);
            return View(model);
        }

        [HttpPost, ActionName("AddShipment")]
        [FormValueRequired(FormValueRequirement.StartsWith, "btnDelete")]
        public ActionResult DeletePackage(int orderId, FormCollection form)
        {
            var order = _orderService.GetOrderById(orderId);
            if (order == null)
                //No order found with the specified id
                return RedirectToAction("List");

            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageOrders);
            if (curManagedStoreId != Store.AllStoreReservedId && _orderService.GetStoreIdByOrderId(orderId) != curManagedStoreId)
            {
                return AccessDeniedView();
            }

            var packages = _httpContext.Session["packages"] as List<ShipmentPackageModel>;
            if (packages == null || packages.Count == 0)
            {
                return RedirectToAction("AddShipment", new { orderId = orderId });
            }

            int packageToDel = -1;
            foreach (var formValue in form.AllKeys)
                if (formValue.StartsWith("btnDelete", StringComparison.InvariantCultureIgnoreCase))
                    int.TryParse(formValue.Substring("btnDelete".Length), out packageToDel);
            if (packageToDel < 0 || packageToDel >= packages.Count)
            {
                return RedirectToAction("AddShipment", new { orderId = orderId });
            }
            else
            {
                packages.RemoveAt(packageToDel);
                _httpContext.Session["packages"] = packages;
            }

            var model = new ShipmentModel()
            {
                OrderId = order.Id,
            };

            ModelState.Clear();
            model.Packages = packages;
            PrepareAddShipmentModel(model, order);

            return View(model);
        }

        [HttpPost, ActionName("AddShipment")]
        [FormValueRequired("ShipmentSubmit")]
        public ActionResult SubmitShipmentOrder(int orderId, ShipmentModel model)
        {
            try
            {
                int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageOrders);

                var order = _orderService.GetOrderById(orderId);
                if (order == null)
                    //No order found with the specified id
                    return RedirectToAction("List");

                if (curManagedStoreId != Store.AllStoreReservedId && _orderService.GetStoreIdByOrderId(orderId) != curManagedStoreId)
                {
                    return AccessDeniedView();
                }

                var packages = model.Packages as List<ShipmentPackageModel>;

                if (packages == null || packages.Count == 0)
                {
                    ErrorNotification("No packages");
                    return RedirectToAction("AddShipment", new { orderId = orderId });
                }

                var customer = _workContext.CurrentCustomer;
                Address from = customer.ShippingAddress;
                Address to = order.ShippingAddress;
                var pickUpDistrict = _logisticsService.GetDistrictByZipPostalCode(from.ZipPostalCode);

                if (from == null || to == null)
                {
                    return RedirectToAction("Edit", new { id = orderId });
                }
                else if (pickUpDistrict == null)
                {
                    ErrorNotification(String.Format(_localizationService.GetResource("Admin.Order.Pickup.District.Notnull"), from.ZipPostalCode));
                    return RedirectToAction("Edit", new { id = orderId });
                }

                #region Third Party Shipping
                if (model.IsThirdPartyShippingOption)
                {
                    var errors = new List<string>();
                    var piList = new List<PackageInformationItems>();
                    var srcm = _shippingService.LoadShippingRateComputationMethodBySystemName(model.ShippingRateComputationMethodSystemName);
                    foreach (var pkg in packages)
                    {
                        if (pkg.Length > 0 && pkg.Width > 0 && pkg.Height > 0 && pkg.Weight > 0)
                        {
                            var pi = new PackageInformation()
                            {
                                Length = pkg.Length,
                                Width = pkg.Width,
                                Height = pkg.Height,
                                Weight = pkg.Weight,
                                WeightMeasureId = pkg.WeightMeasureId,
                                DimensionMeasureId = pkg.DimensionMeasureId
                            };
                            var items = new List<ShoppingCartItem>();
                            foreach (var p in pkg.Products)
                            {
                                items.Add(new ShoppingCartItem()
                                {
                                    ProductVariantId = p.ProductVariantId,
                                    Quantity = p.QuantityToAdd
                                });
                            }
                            pi = _logisticsOrderProcessService.GetConvertedPackage(pi);
                            if (!srcm.ValidateDimensionAndWeight(pi))
                            {
                                errors.Add("Package (L + W + H) or weight oversize.");
                            }
                            piList.Add(new PackageInformationItems()
                            {
                                PackageInformation = pi,
                                Items = items
                            });
                        }
                        else
                        {
                            errors.Add(String.Format(_localizationService.GetResource("Admin.Order.Package.InvalidDimensionWeight"), pkg.PackageId));
                        }
                    }
                    if (errors.Count == 0)
                    {
                        var getShippingOptionRequest = _shippingService.CreateShippingOptionRequest(piList, customer, to, from);
                        var getShippingOptionResponse = srcm.GetShippingOptions(getShippingOptionRequest);
                        //log errors
                        if (!getShippingOptionResponse.Success)
                        {
                            foreach (string error in getShippingOptionResponse.Errors)
                            {
                                errors.Add(string.Format("Shipping ({0}). {1}", srcm.PluginDescriptor.FriendlyName, error));
                            }
                        }
                        else
                        {
                            order.ShippingStatus = ShippingStatus.ReadyForPickUp;
                            var so2 = getShippingOptionResponse.ShippingOptions.First();
                            so2.ShippingRateComputationMethodSystemName = srcm.PluginDescriptor.SystemName;
                            so2.Rate = Nop.Services.Common.MathEx.Round(so2.Rate, 2);
                            String rate = _priceFormatter.FormatPrice(so2.Rate);
                            var shippingOption = new ThirdPartyShippingOption()
                            {
                                Order = order,
                                CreatedOnUTC = DateTime.UtcNow,
                                CustomerEmail = customer.Email,
                                BillingAddress = from.FormattedAddressNoSuite(),
                                ShippingAddress = to.FormattedAddressNoSuite(),
                                Name = so2.Name,
                                ShippingRateComputationMethodSystemName = so2.ShippingRateComputationMethodSystemName,
                                Rate = so2.Rate,
                                PackageCount = packages.Count()
                            };

                            order.ThirdPartyShippingOptions.Add(shippingOption);
                            order.OrderNotes.Add(new OrderNote()
                            {
                                CreatedOnUtc = DateTime.UtcNow,
                                OrderId = order.Id,
                                DisplayToCustomer = false,
                                Note = String.Format(_localizationService.GetResource("Admin.Order.ThirdPartyShippingOption"), so2.Name, rate, customer.Email, packages.Count())
                            });

                            var baseWeight = _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId);
                            var baseWeightIn = baseWeight != null ? baseWeight.Name : "";
                            var baseDimension = _measureService.GetMeasureDimensionById(_measureSettings.BaseDimensionId);
                            var baseDimensionIn = baseDimension != null ? baseDimension.Name : "";
                            foreach (var p in piList)
                            {
                                //add package information
                                order.OrderNotes.Add(new OrderNote()
                                {
                                    CreatedOnUtc = DateTime.UtcNow,
                                    OrderId = order.Id,
                                    DisplayToCustomer = false,
                                    Note = String.Format("package {0} measure: {2:###.##}{1} x {3:###.##}{1} x {4:###.##}{1} {5:##.##}{6}", p.PackageInformation.Id, baseDimensionIn,
                                                        MathEx.Round(p.PackageInformation.Length.Value, 2), MathEx.Round(p.PackageInformation.Width.Value, 2), MathEx.Round(p.PackageInformation.Height.Value, 2),
                                                        MathEx.Round(p.PackageInformation.Weight.Value, 2), baseWeightIn)
                                });
                            }
                            _orderService.UpdateOrder(order);
                            SuccessNotification(_localizationService.GetResource("Admin.Order.ThirdPartyShippingOption.PlacePackages"));
                            return RedirectToAction("Edit", new { id = orderId });
                        }
                    }

                    if (errors.Count > 0)
                    {
                        foreach (var e in errors)
                            ErrorNotification(e);
                        return RedirectToAction("Edit", new { id = orderId });
                    }

                    return RedirectToAction("Edit", new { id = orderId });
                }
                #endregion

                var oi = new OrderInformation()
                {
                    UserAddressId = from.Id,
                    Locations = new List<LocationInformation>(),
                    IsCommerce = true,
                    OrderKindId = (int)order.OrderKindId
                };


                var li = new LocationInformation()
                {
                    PackageNumber = 1,
                    IsUserAddressPickup = true,
                    AddressId = to.Id,
                    Packages = new List<PackageInformation>()
                };

                var shipments = new List<Shipment>();
                for (int i = 0; i < packages.Count; i++)
                {
                    var pkg = packages[i];
                    li.Packages.Add(new PackageInformation()
                    {
                        Length = pkg.Length == 0M ? null : (decimal?)pkg.Length,
                        Width = pkg.Width == 0M ? null : (decimal?)pkg.Width,
                        Height = pkg.Height == 0M ? null : (decimal?)pkg.Height,
                        Weight = pkg.Weight == 0M ? null : (decimal?)pkg.Weight,
                        IsLettersize = pkg.Length == 0M
                    });

                    //add E-commerce attribute
                    li.Packages[i].AddtionalAttributes |= AddtionalAttributes.ECommerce;

                    if (pkg.IsVertical)
                    {
                        li.Packages[i].AddtionalAttributes |= AddtionalAttributes.VerticalHandle;
                    }
                    else
                    {
                        li.Packages[i].AddtionalAttributes &= ~AddtionalAttributes.VerticalHandle;
                    }
                    if (pkg.IsFragile)
                    {
                        li.Packages[i].AddtionalAttributes |= AddtionalAttributes.Fragile;
                    }
                    else
                    {
                        li.Packages[i].AddtionalAttributes &= ~AddtionalAttributes.Fragile;
                    }
                    if (pkg.IsDangerous)
                    {
                        li.Packages[i].AddtionalAttributes |= AddtionalAttributes.DangerousGood;
                    }
                    else
                    {
                        li.Packages[i].AddtionalAttributes &= ~AddtionalAttributes.DangerousGood;
                    }

                    var shipment = new Shipment()
                    {
                        OrderId = order.Id,
                        Length = pkg.Length,
                        Width = pkg.Width,
                        Height = pkg.Height,
                        TotalWeight = pkg.Weight,
                        ShippedDateUtc = null,
                        DeliveryDateUtc = null,
                        CreatedOnUtc = DateTime.UtcNow,
                        TrackingNumber = string.Empty
                    };

                    foreach (var p in pkg.Products)
                    {
                        var sopv = new ShipmentOrderProductVariant()
                        {
                            OrderProductVariantId = p.OrderProductVariantId,
                            Quantity = p.QuantityToAdd,
                        };
                        shipment.ShipmentOrderProductVariants.Add(sopv);
                    }
                    _shipmentService.InsertShipment(shipment);
                    shipments.Add(shipment);
                }
                //create a shipment order product variant
                oi.Locations.Add(li);

                int logisticsOrderId = _logisticsOrderProcessService.PlaceOrder(oi, customer, order.Id, true, true, true);

                if (logisticsOrderId > 0)
                {
                    //send email
                    _logisticsOrderProcessService.SendLogisticPlacedNotification(logisticsOrderId);

                    var logisticsOrder = _orderService.GetOrderById(logisticsOrderId);
                    if (logisticsOrder != null && li.Packages.Count == packages.Count)
                    {
                        order.ShippingStatus = ShippingStatus.ReadyForPickUp;

                        _orderService.UpdateOrder(order);
                        SuccessNotification(_localizationService.GetResource("Admin.Orders.Shipments.Added"));

                        for (int i = 0; i < li.Packages.Count; i++)
                        {
                            var pkg = li.Packages[i];
                            var shipment = shipments[i];
                            shipment.PackageId = pkg.PackageId;
                            shipment.TrackingNumber = pkg.TrackingNumber;
                            _shipmentService.UpdateShipment(shipment);
                        }
                        return RedirectToAction("Edit", new { id = orderId });
                    }

                    // TODO: Handle exception here
                    //SuccessNotification(_localizationService.GetResource("Admin.Orders.Shipments.Added"));
                    //return continueEditing
                    //           ? RedirectToAction("ShipmentDetails", new {id = shipment.Id})
                    //           : RedirectToAction("Edit", new { id = orderId });
                }
                ErrorNotification(_localizationService.GetResource("Admin.Orders.Shipments.LogisticsOrderFail"));
            }
            catch (Exception exc)
            {
                //error
                ErrorNotification(exc, true);
            }
            return RedirectToAction("AddShipment", new { orderId = orderId });
        }

        //[HttpPost, ParameterBasedOnFormNameAttribute("save-continue", "continueEditing")]
        //[FormValueRequired("save", "save-continue")]
        ////public ActionResult AddShipment(int orderId, FormCollection form, bool continueEditing)
        //public ActionResult AddShipment(int orderId, FormCollection form, bool continueEditing)
        //{
        //    int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageOrders);

        //    var order = _orderService.GetOrderById(orderId);
        //    if (order == null)
        //        //No order found with the specified id
        //        return RedirectToAction("List");

        //    if (curManagedStoreId != Store.AllStoreReservedId && _orderService.GetStoreIdByOrderId(orderId) != curManagedStoreId)
        //    {
        //        return AccessDeniedView();
        //    }

        //    Shipment shipment = null;

        //    decimal? totalWeight = null;
        //    foreach (var opv in order.OrderProductVariants)
        //    {
        //        //is shippable
        //        if (!opv.ProductVariant.IsShipEnabled)
        //            continue;

        //        //ensure that this product variant can be shipped (have at least one item to ship)
        //        var maxQtyToAdd = opv.GetTotalNumberOfItemsCanBeAddedToShipment();
        //        if (maxQtyToAdd <= 0)
        //            continue;

        //        int qtyToAdd = 0; //parse quantity
        //        foreach (string formKey in form.AllKeys)
        //            if (formKey.Equals(string.Format("qtyToAdd{0}", opv.Id), StringComparison.InvariantCultureIgnoreCase))
        //            {
        //                int.TryParse(form[formKey], out qtyToAdd);
        //                break;
        //            }

        //        //validate quantity
        //        if (qtyToAdd <= 0)
        //            continue;
        //        if (qtyToAdd > maxQtyToAdd)
        //            qtyToAdd = maxQtyToAdd;

        //        //ok. we have at least one item. let's create a shipment (if it does not exist)

        //        var opvTotalWeight = opv.ItemWeight.HasValue ? opv.ItemWeight * qtyToAdd : null;
        //        if (opvTotalWeight.HasValue)
        //        {
        //            if (!totalWeight.HasValue)
        //                totalWeight = 0;
        //            totalWeight += opvTotalWeight.Value;
        //        }
        //        if (shipment == null)
        //        {
        //            shipment = new Shipment()
        //            {
        //                OrderId = order.Id,
        //                //TrackingNumber = form["TrackingNumber"],
        //                TotalWeight = null,
        //                ShippedDateUtc = null,
        //                DeliveryDateUtc = null,
        //                CreatedOnUtc = DateTime.UtcNow,
        //            };
        //        }
        //        //create a shipment order product variant
        //        var sopv = new ShipmentOrderProductVariant()
        //        {
        //            OrderProductVariantId = opv.Id,
        //            Quantity = qtyToAdd,
        //        };
        //        shipment.ShipmentOrderProductVariants.Add(sopv);
        //    }

        //    //if we have at least one item in the shipment, then save it
        //    if (shipment != null && shipment.ShipmentOrderProductVariants.Count > 0)
        //    {
        //        var appCustomer = _workContext.CurrentCustomer;
        //        shipment.TotalWeight = totalWeight;
        //        Address from = appCustomer.ShippingAddress;

        //        if (from == null)
        //        {
        //            from = appCustomer.Addresses.FirstOrDefault();
        //        }

        //        if (from == null)
        //        {
        //            ErrorNotification(_localizationService.GetResource("Admin.Orders.Shipments.NoShippingAddress"));
        //            return RedirectToAction("AddShipment", new { orderId = orderId });
        //        }

        //        Address to = order.ShippingAddress;

        //        var oi = new OrderInformation()
        //        {
        //            UserAddressId = from.Id,
        //            Locations = new List<LocationInformation>()
        //        };
        //        var li = new LocationInformation()
        //        {
        //            PackageNumber = 1,
        //            IsUserAddressPickup = true,
        //             AddressId = to.Id,
        //              Packages = new List<PackageInformation>()
        //        };
        //        li.Packages.Add(new PackageInformation()
        //        {
        //            Length = shipment.Length,
        //            Width = shipment.Width,
        //            Height = shipment.Height,
        //            Weight = shipment.TotalWeight
        //        });
        //        oi.Locations.Add(li);

        //        int logisticsOrderId = _logisticsOrderProcessService.PlaceOrder(oi, appCustomer, orderId);
        //        if (logisticsOrderId > 0)
        //        {
        //            order.ShippingStatus = ShippingStatus.ReadyForPickUp;
        //            _orderService.UpdateOrder(order);
        //            SuccessNotification(_localizationService.GetResource("Admin.Orders.Shipments.Added"));
        //        }

        //        return RedirectToAction("Edit", new { id = orderId });

        //        // TODO: Handle exception here
        //        //SuccessNotification(_localizationService.GetResource("Admin.Orders.Shipments.Added"));
        //        //return continueEditing
        //        //           ? RedirectToAction("ShipmentDetails", new {id = shipment.Id})
        //        //           : RedirectToAction("Edit", new { id = orderId });
        //    }
        //    else
        //    {
        //        ErrorNotification(_localizationService.GetResource("Admin.Orders.Shipments.NoProductsSelected"));
        //        return RedirectToAction("AddShipment", new { orderId = orderId });
        //    }
        //}

        public ActionResult ShipmentDetails(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var shipment = _shipmentService.GetShipmentById(id);
            if (shipment == null)
                //No shipment found with the specified id
                return RedirectToAction("List");

            var model = PrepareShipmentModel(shipment, true);
            return View(model);
        }

        [HttpPost]
        public ActionResult DeleteShipment(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var shipment = _shipmentService.GetShipmentById(id);
            if (shipment == null)
                //No shipment found with the specified id
                return RedirectToAction("List");

            var orderId = shipment.OrderId;

            SuccessNotification(_localizationService.GetResource("Admin.Orders.Shipments.Deleted"));
            _shipmentService.DeleteShipment(shipment);
            return RedirectToAction("Edit", new { id = orderId });
        }

        [HttpPost, ActionName("ShipmentDetails")]
        [FormValueRequired("settrackingnumber")]
        public ActionResult SetTrackingNumber(ShipmentModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var shipment = _shipmentService.GetShipmentById(model.Id);
            if (shipment == null)
                //No shipment found with the specified id
                return RedirectToAction("List");

            shipment.TrackingNumber = model.TrackingNumber;
            _shipmentService.UpdateShipment(shipment);

            return RedirectToAction("ShipmentDetails", new { id = shipment.Id });
        }

        [HttpPost, ActionName("ShipmentDetails")]
        [FormValueRequired("setasshipped")]
        public ActionResult SetAsShipped(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var shipment = _shipmentService.GetShipmentById(id);
            if (shipment == null)
                //No shipment found with the specified id
                return RedirectToAction("List");

            try
            {
                _orderProcessingService.Ship(shipment, true);
                return RedirectToAction("ShipmentDetails", new { id = shipment.Id });
            }
            catch (Exception exc)
            {
                //error
                ErrorNotification(exc, true);
                return RedirectToAction("ShipmentDetails", new { id = shipment.Id });
            }
        }

        [HttpPost, ActionName("ShipmentDetails")]
        [FormValueRequired("setasdelivered")]
        public ActionResult SetAsDelivered(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var shipment = _shipmentService.GetShipmentById(id);
            if (shipment == null)
                //No shipment found with the specified id
                return RedirectToAction("List");

            try
            {
                _orderProcessingService.Deliver(shipment, true);
                return RedirectToAction("ShipmentDetails", new { id = shipment.Id });
            }
            catch (Exception exc)
            {
                //error
                ErrorNotification(exc, true);
                return RedirectToAction("ShipmentDetails", new { id = shipment.Id });
            }
        }

        public ActionResult PdfPackagingSlip(int shipmentId)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var shipment = _shipmentService.GetShipmentById(shipmentId);
            if (shipment == null)
                //no shipment found with the specified id
                return RedirectToAction("List");

            var order = shipment.Order;

            var shipments = new List<Shipment>();
            shipments.Add(shipment);

            byte[] bytes = null;
            using (var stream = new MemoryStream())
            {
                _pdfService.PrintPackagingSlipsToPdf(stream, shipments, _workContext.WorkingLanguage);
                bytes = stream.ToArray();
            }
            return File(bytes, "application/pdf", string.Format("packagingslip_{0}.pdf", shipment.Id));
        }

        public ActionResult PdfPackagingSlipAll()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var shipments = _shipmentService.GetAllShipments(null, null, 0, int.MaxValue);

            byte[] bytes = null;
            using (var stream = new MemoryStream())
            {
                _pdfService.PrintPackagingSlipsToPdf(stream, shipments, _workContext.WorkingLanguage);
                bytes = stream.ToArray();
            }
            return File(bytes, "application/pdf", "packagingslips.pdf");
        }

        public ActionResult PdfPackagingSlipSelected(string selectedIds)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var shipments = new List<Shipment>();
            if (selectedIds != null)
            {
                var ids = selectedIds
                    .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                    .Select(x => Convert.ToInt32(x))
                    .ToArray();
                shipments.AddRange(_shipmentService.GetShipmentsByIds(ids));
            }

            byte[] bytes = null;
            using (var stream = new MemoryStream())
            {
                _pdfService.PrintPackagingSlipsToPdf(stream, shipments, _workContext.WorkingLanguage);
                bytes = stream.ToArray();
            }
            return File(bytes, "application/pdf", "packagingslips.pdf");
        }

        public ActionResult PrintPackageWaybillAsPdf(int shipmentId)
        {
            Shipment shipment;
            try
            {
                shipment = _shipmentService.GetShipmentById(shipmentId);
            }
            catch
            {
                ErrorNotification(String.Format("Error retrieving Shipment with ID: {0}", shipmentId));
                return RedirectToAction("ShipmentDetails", new { Id = shipmentId });
            }

            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageOrders);
            if (curManagedStoreId != Store.AllStoreReservedId && _orderService.GetStoreIdByOrderId(shipment.OrderId) != curManagedStoreId)
            {
                return AccessDeniedView();
            }

            try
            {
                LogisticsPackage pkg;

                if (shipment.PackageId.HasValue)
                {
                    pkg = _logisticsService.GetPackageById(shipment.PackageId.Value);
                }
                else
                {
                    pkg = _logisticsService.GetPackageByShipmentId(shipmentId);
                }
                if (pkg == null)
                {
                    ErrorNotification(String.Format("Error retrieving package within shipment ID: {0}", shipmentId));
                    return RedirectToAction("ShipmentDetails", new { Id = shipmentId });
                }

                var trackingUrl = Url.RouteUrl("LogisticsTrack", new { pid = pkg.Id }, Request.Url.Scheme);

                byte[] bytes = null;
                using (var stream = new MemoryStream())
                {
                    _pdfService.PrintLogisticsWaybillToPdf(stream, pkg, trackingUrl, _workContext.WorkingLanguage);
                    bytes = stream.ToArray();
                }
                return File(bytes, "application/pdf", Nop.Core.Domain.Logistics.WaybillFilename.GenerateWaybillFilename(pkg.Shipment.TrackingNumber));
            }
            catch (Exception exc)
            {
                ErrorNotification(exc);
                return RedirectToAction("ShipmentDetails", new { Id = shipmentId });
            }
        }


        #endregion

        #region Order notes

        [HttpPost, GridAction(EnableCustomBinding = true)]
        public ActionResult OrderNotesSelect(int orderId, GridCommand command)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(orderId);
            if (order == null)
                throw new ArgumentException("No order found with the specified id");

            //order notes
            var orderNoteModels = new List<OrderModel.OrderNote>();
            foreach (var orderNote in order.OrderNotes
                .OrderByDescending(on => on.CreatedOnUtc))
            {
                orderNoteModels.Add(new OrderModel.OrderNote()
                {
                    Id = orderNote.Id,
                    OrderId = orderNote.OrderId,
                    DisplayToCustomer = orderNote.DisplayToCustomer,
                    Note = orderNote.FormatOrderNoteText(),
                    CreatedOn = _dateTimeHelper.ConvertToUserTime(orderNote.CreatedOnUtc, DateTimeKind.Utc)
                });
            }

            var model = new GridModel<OrderModel.OrderNote>
            {
                Data = orderNoteModels,
                Total = orderNoteModels.Count
            };

            return new JsonResult
            {
                Data = model
            };
        }

        [ValidateInput(false)]
        public ActionResult OrderNoteAdd(int orderId, bool displayToCustomer, string message)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(orderId);
            if (order == null)
                return Json(new { Result = false }, JsonRequestBehavior.AllowGet);

            var orderNote = new OrderNote()
            {
                DisplayToCustomer = displayToCustomer,
                Note = message,
                CreatedOnUtc = DateTime.UtcNow,
            };
            order.OrderNotes.Add(orderNote);
            _orderService.UpdateOrder(order);

            //new order notification
            if (displayToCustomer)
            {
                //email
                //_workflowMessageService.SendNewOrderNoteAddedCustomerNotification(
                //    orderNote, _workContext.WorkingLanguage.Id);

            }

            return Json(new { Result = true }, JsonRequestBehavior.AllowGet);
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult OrderNoteDelete(int orderId, int orderNoteId, GridCommand command)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageOrders))
                return AccessDeniedView();

            var order = _orderService.GetOrderById(orderId);
            if (order == null)
                throw new ArgumentException("No order found with the specified id");

            var orderNote = order.OrderNotes.Where(on => on.Id == orderNoteId).FirstOrDefault();
            if (orderNote == null)
                throw new ArgumentException("No order note found with the specified id");
            _orderService.DeleteOrderNote(orderNote);

            return OrderNotesSelect(orderId, command);
        }


        #endregion

        #region Reports

        [NonAction]
        protected IList<BestsellersReportLineModel> GetBestsellersBriefReportModel(int recordsToReturn, int orderBy, int curManagedStoreId)
        {
            //group by product variants (not products)
            var report = _orderReportService.BestSellersReport(null, null,
                null, null, null, 0, 0, curManagedStoreId, 0, recordsToReturn, orderBy, 1, true);

            var model = report.Select(x =>
            {
                var m = new BestsellersReportLineModel()
                {
                    ProductVariantId = x.EntityId,
                    TotalAmount = _priceFormatter.FormatPrice(x.TotalAmount, true, false),
                    TotalQuantity = x.TotalQuantity,
                };
                var productVariant = _productService.GetProductVariantById(x.EntityId);
                if (productVariant != null)
                    m.ProductVariantFullName = productVariant.Product.Name + " " + productVariant.Name;
                return m;
            }).ToList();

            return model;
        }
        public ActionResult BestsellersBriefReportByQuantity()
        {
            int curManagedStoreId;
            bool canManageAllStore, isAccessDenied;
            _permissionService.SecurityAccessForStoreManager(out curManagedStoreId, out canManageAllStore, out isAccessDenied);
            if (isAccessDenied)
                return AccessDeniedView();

            var model = GetBestsellersBriefReportModel(5, 1, curManagedStoreId);
            return PartialView(model);
        }
        [GridAction(EnableCustomBinding = true)]
        public ActionResult BestsellersBriefReportByQuantityList(GridCommand command)
        {
            int curManagedStoreId;
            bool canManageAllStore, isAccessDenied;
            _permissionService.SecurityAccessForStoreManager(out curManagedStoreId, out canManageAllStore, out isAccessDenied);
            if (isAccessDenied)
                return AccessDeniedView();

            var model = GetBestsellersBriefReportModel(5, 1, curManagedStoreId);
            var gridModel = new GridModel<BestsellersReportLineModel>
            {
                Data = model,
                Total = model.Count
            };
            return new JsonResult
            {
                Data = gridModel
            };
        }

        public ActionResult BestsellersBriefReportByAmount()
        {
            int curManagedStoreId;
            bool canManageAllStore, isAccessDenied;
            _permissionService.SecurityAccessForStoreManager(out curManagedStoreId, out canManageAllStore, out isAccessDenied);
            if (isAccessDenied)
                return AccessDeniedView();

            var model = GetBestsellersBriefReportModel(5, 2, curManagedStoreId);
            return PartialView(model);
        }

        [GridAction(EnableCustomBinding = true)]
        public ActionResult BestsellersBriefReportByAmountList(GridCommand command)
        {
            int curManagedStoreId;
            bool canManageAllStore, isAccessDenied;
            _permissionService.SecurityAccessForStoreManager(out curManagedStoreId, out canManageAllStore, out isAccessDenied);
            if (isAccessDenied)
                return AccessDeniedView();

            var model = GetBestsellersBriefReportModel(5, 2, curManagedStoreId);
            var gridModel = new GridModel<BestsellersReportLineModel>
            {
                Data = model,
                Total = model.Count
            };
            return new JsonResult
            {
                Data = gridModel
            };
        }

        public ActionResult BestsellersReport()
        {
            var model = new BestsellersReportModel();

            //order statuses
            model.AvailableOrderStatuses = OrderStatus.Pending.ToSelectList(false).ToList();
            model.AvailableOrderStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });

            //payment statuses
            model.AvailablePaymentStatuses = PaymentStatus.Pending.ToSelectList(false).ToList();
            model.AvailablePaymentStatuses.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });

            //billing countries
            foreach (var c in _countryService.GetAllCountriesForBilling())
            {
                model.AvailableCountries.Add(new SelectListItem() { Text = c.Name, Value = c.Id.ToString() });
            }
            model.AvailableCountries.Insert(0, new SelectListItem() { Text = _localizationService.GetResource("Admin.Address.SelectCountry"), Value = "0" });

            return View(model);
        }
        [GridAction(EnableCustomBinding = true)]
        public ActionResult BestsellersReportList(GridCommand command, BestsellersReportModel model)
        {
            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageOrders);
            if (curManagedStoreId == 0)
            {
                return AccessDeniedView();
            }

            DateTime? startDateValue = (model.StartDate == null) ? null
                            : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.StartDate.Value, _dateTimeHelper.CurrentTimeZone);

            DateTime? endDateValue = (model.EndDate == null) ? null
                            : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.EndDate.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1);

            OrderStatus? orderStatus = model.OrderStatusId > 0 ? (OrderStatus?)(model.OrderStatusId) : null;
            PaymentStatus? paymentStatus = model.PaymentStatusId > 0 ? (PaymentStatus?)(model.PaymentStatusId) : null;

            //return first 100 records
            //group by product variants (not products)
            var items = _orderReportService.BestSellersReport(startDateValue, endDateValue,
                orderStatus, paymentStatus, null, model.BillingCountryId, 0, curManagedStoreId, 0, 100, 2, 1, true);
            var gridModel = new GridModel<BestsellersReportLineModel>
            {
                Data = items.Select(x =>
                {
                    var m = new BestsellersReportLineModel()
                    {
                        ProductVariantId = x.EntityId,
                        TotalAmount = _priceFormatter.FormatPrice(x.TotalAmount, true, false),
                        TotalQuantity = x.TotalQuantity,
                    };
                    var productVariant = _productService.GetProductVariantById(x.EntityId);
                    if (productVariant != null)
                        m.ProductVariantFullName = productVariant.Product.Name + " " + productVariant.Name;
                    return m;
                }),
                Total = items.Count
            };
            return new JsonResult
            {
                Data = gridModel
            };
        }

        public ActionResult NeverSoldReport()
        {
            var model = new NeverSoldReportModel();
            return View(model);
        }
        [GridAction(EnableCustomBinding = true)]
        public ActionResult NeverSoldReportList(GridCommand command, NeverSoldReportModel model)
        {
            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageOrders);
            if (curManagedStoreId == 0)
            {
                return AccessDeniedView();
            }

            DateTime? startDateValue = (model.StartDate == null) ? null
                            : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.StartDate.Value, _dateTimeHelper.CurrentTimeZone);

            DateTime? endDateValue = (model.EndDate == null) ? null
                            : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.EndDate.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1);

            var items = _orderReportService.ProductsNeverSold(startDateValue, endDateValue,
                command.Page - 1, command.PageSize, true, curManagedStoreId);
            var gridModel = new GridModel<NeverSoldReportLineModel>
            {
                Data = items.Select(x =>
                    new NeverSoldReportLineModel()
                    {
                        ProductVariantId = x.Id,
                        ProductVariantFullName = x.Product.Name + " " + x.Name,
                    }),
                Total = items.TotalCount
            };
            return new JsonResult
            {
                Data = gridModel
            };
        }


        [NonAction]
        protected virtual IList<OrderAverageReportLineSummaryModel> GetOrderAverageReportModel(int curManagedStoreId)
        {
            var report = new List<OrderAverageReportLineSummary>();
            //report.Add(_orderReportService.OrderAverageReport(OrderStatus.Pending, curManagedStoreId));
            //report.Add(_orderReportService.OrderAverageReport(OrderStatus.Processing, curManagedStoreId));
            //report.Add(_orderReportService.OrderAverageReport(OrderStatus.Complete, curManagedStoreId));
            //report.Add(_orderReportService.OrderAverageReport(OrderStatus.Cancelled, curManagedStoreId));
            var model = report.Select(x =>
            {
                return new OrderAverageReportLineSummaryModel()
                {
                    OrderStatus = x.OrderStatus.GetLocalizedEnum(_localizationService, _workContext),
                    SumTodayOrders = _priceFormatter.FormatPrice(x.SumTodayOrders, true, false),
                    SumThisWeekOrders = _priceFormatter.FormatPrice(x.SumThisWeekOrders, true, false),
                    SumThisMonthOrders = _priceFormatter.FormatPrice(x.SumThisMonthOrders, true, false),
                    SumThisYearOrders = _priceFormatter.FormatPrice(x.SumThisYearOrders, true, false),
                    SumAllTimeOrders = _priceFormatter.FormatPrice(x.SumAllTimeOrders, true, false),
                };
            }).ToList();

            return model;
        }
        [ChildActionOnly]
        public ActionResult OrderAverageReport()
        {
            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageOrders, false);
            if (curManagedStoreId == 0)
            {
                return AccessDeniedView();
            }
            var model = GetOrderAverageReportModel(curManagedStoreId);
            return PartialView(model);
        }
        [GridAction(EnableCustomBinding = true)]
        public ActionResult OrderAverageReportList(GridCommand command)
        {
            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageOrders, false);
            if (curManagedStoreId == 0)
            {
                return AccessDeniedView();
            }
            var model = GetOrderAverageReportModel(curManagedStoreId);
            var gridModel = new GridModel<OrderAverageReportLineSummaryModel>
            {
                Data = model,
                Total = model.Count
            };
            return new JsonResult
            {
                Data = gridModel
            };
        }

        [NonAction]
        protected virtual IList<OrderIncompleteReportLineModel> GetOrderIncompleteReportModel(int curManagedStoreId)
        {
            var model = new List<OrderIncompleteReportLineModel>();
            //not paid
            var psPending = _orderReportService.GetOrderAverageReportLine(null, new PaymentStatus[] { PaymentStatus.Pending }, null, null, null, null, null, curManagedStoreId, true);
            model.Add(new OrderIncompleteReportLineModel()
            {
                Item = _localizationService.GetResource("Admin.SalesReport.Incomplete.TotalUnpaidOrders"),
                Count = psPending.CountOrders,
                Total = _priceFormatter.FormatPrice(psPending.SumOrders, true, false)
            });
            //not shipped
            var ssPending = _orderReportService.GetOrderAverageReportLine(null, null, ShippingStatus.NotYetShipped, null, null, null, null, curManagedStoreId, true);
            model.Add(new OrderIncompleteReportLineModel()
            {
                Item = _localizationService.GetResource("Admin.SalesReport.Incomplete.TotalNotShippedOrders"),
                Count = ssPending.CountOrders,
                Total = _priceFormatter.FormatPrice(ssPending.SumOrders, true, false)
            });
            //pending
            var osPending = _orderReportService.GetOrderAverageReportLine(OrderStatus.Pending, null, null, null, null, null, null, curManagedStoreId, true);
            model.Add(new OrderIncompleteReportLineModel()
            {
                Item = _localizationService.GetResource("Admin.SalesReport.Incomplete.TotalIncompleteOrders"),
                Count = osPending.CountOrders,
                Total = _priceFormatter.FormatPrice(osPending.SumOrders, true, false)
            });
            return model;
        }
        [ChildActionOnly]
        public ActionResult OrderIncompleteReport()
        {
            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageOrders, false);
            if (curManagedStoreId == 0)
            {
                return AccessDeniedView();
            }
            var model = GetOrderIncompleteReportModel(curManagedStoreId);
            return PartialView(model);
        }
        [GridAction(EnableCustomBinding = true)]
        public ActionResult OrderIncompleteReportList(GridCommand command)
        {
            int curManagedStoreId = _permissionService.GetAuthorizedStore(StandardPermissionProvider.ManageOrders, false);
            if (curManagedStoreId == 0)
            {
                return AccessDeniedView();
            }
            var model = GetOrderIncompleteReportModel(curManagedStoreId);
            var gridModel = new GridModel<OrderIncompleteReportLineModel>
            {
                Data = model,
                Total = model.Count
            };
            return new JsonResult
            {
                Data = gridModel
            };
        }

        #endregion

        #region Shipment Date

        [HttpPost]
        public string EditShipmnetDate(string orderId, string shipDate, string rangeId, string desc)
        {
            if (string.IsNullOrEmpty(orderId) || string.IsNullOrEmpty(shipDate) || string.IsNullOrEmpty(rangeId)) return "Field error";

            var date = Convert.ToDateTime(shipDate).Date;
            var shiptimerangeId = int.Parse(rangeId);

            Order order = _orderService.GetOrderById(Convert.ToInt32(orderId));

            if (order == null) return "order can not find";

            order.ShipmentDate = date;
            order.ShippingTimeWindowsId = shiptimerangeId;

            order.OrderNotes.Add(new OrderNote()
              {
                  CreatedOnUtc = DateTime.UtcNow,
                  DisplayToCustomer = false,
                  Note = string.Format("\"{0}\" update the estimated delivery time,{1}", _workContext.CurrentCustomer.GetFullName(), desc),
                  OrderId = Convert.ToInt32(orderId)
              });

            _orderService.UpdateOrder(order);

            UpdateLogisLogisticsPackageShipmentDate(order.Shipments.Select(d => d.Id).ToArray(), shiptimerangeId, date);

            return "update success!!";
        }

        private void UpdateLogisLogisticsPackageShipmentDate(int[] shipmentIds, int shipingTimeWindowId, DateTime shipDate)
        {
            var shipmentAddresss = _logisticsService.GetPackagesByShipmentIds(shipmentIds);

            if (shipmentAddresss == null || shipmentAddresss.Count <= 0) return;

            var shippingTimeWindow = _logisticsService.GetShippingTimeWindowById(shipingTimeWindowId);

            foreach (var item in shipmentAddresss)
            {
                item.ReadyOnUtc = new DateTime(shipDate.Year, shipDate.Month, shipDate.Day, shippingTimeWindow.NoEarlyThan, 0, 0);
                item.PickupEndOnUtc = new DateTime(shipDate.Year, shipDate.Month, shipDate.Day, shippingTimeWindow.NoLateThan, 0, 0);
                _logisticsService.UpdatePackage(item);
            }
        }

        [HttpPost]
        //no useful
        public string GetAllShipmentTimeWidow()
        {
            var result = _logisticsService.GetAllShippingTimeWindows();
            if (result != null)
            {
                return JsonConvert.SerializeObject(result);
            }
            return string.Empty;
        }

        [HttpPost]
        public string GetAllowShipmentTimeWindow(int orderTypeId,int addressTypeId)
        {
            var result = _logisticsService.GetAllShippingTimeWindows().Where(x => x.AllowAddressType.Contains(addressTypeId.ToString()) && x.AllowOrderType.Contains(orderTypeId.ToString()));
            if (result != null)
            {
                return JsonConvert.SerializeObject(result);
            }
            return string.Empty;
        }
        #endregion

        #region Print Packing
        public ActionResult PrintPackingSummary(int orderId)
        {
            var printPacking = new PrintPackingModel();
            var order = _orderService.GetOrderById(orderId);

            printPacking.Company = order.ShippingAddress.Company;
            printPacking.FirstName = order.ShippingAddress.FirstName;
            printPacking.LastName = order.ShippingAddress.LastName;
            printPacking.Address1 = order.ShippingAddress.Address1;
            printPacking.Address2 = order.ShippingAddress.Address2;
            printPacking.City = order.ShippingAddress.City;
            printPacking.StateProvince = order.ShippingAddress.StateProvince.Abbreviation;
            printPacking.ZipPostalCode = order.ShippingAddress.ZipPostalCode;
            printPacking.Phone = !String.IsNullOrEmpty(order.ShippingAddress.CellPhoneNumber) ? order.ShippingAddress.CellPhoneNumber : order.ShippingAddress.PhoneNumber;
            printPacking.CountryName = order.ShippingAddress.Country.Name;
            printPacking.OrderNumber = order.Id.ToString();
            printPacking.OrderDate = _dateTimeHelper.ConvertToUserTime(order.CreatedOnUtc, DateTimeKind.Utc);
            var productsvartiantList = order.OrderProductVariants.Where(x => (x.IsRefund.HasValue == false || x.IsRefund.Value == 0)).ToList();


            var itemList = productsvartiantList.Select(oneItem => new ItemMessage
            {
                Name = !String.IsNullOrEmpty(oneItem.ProductVariant.Name) ? oneItem.ProductVariant.Name : oneItem.ProductVariant.Product.Name,
                Number = string.IsNullOrEmpty(oneItem.ProductVariant.Gtin) ? "" : oneItem.ProductVariant.Gtin,
                Attributes = _productAttributeFormatter.FormatAttributes(oneItem.ProductVariant, oneItem.AttributesXml),
                Qantity = oneItem.Quantity
            }).ToList();
            printPacking.ItemList = itemList;


            byte[] bytes = null;
            using (var stream = new MemoryStream())
            {
                _pdfService.PrintPacking(stream, printPacking);
                bytes = stream.ToArray();
            }
            return File(bytes, "application/pdf", "PrintPackingSummary_" + orderId + ".pdf");
        }

        #endregion
    }
}